Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
ConfigurableProductsFixture.php
Go to the documentation of this file.
1 <?php
7 namespace Magento\Setup\Fixtures;
8 
11 use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
14 use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory;
19 use Symfony\Component\Console\Output\OutputInterface;
20 
78 {
82  protected $priority = 50;
83 
87  private $searchConfig;
88 
92  private $dataGenerator;
93 
97  private $attributeSetsFixture;
98 
102  private $attributePattern;
103 
107  private $productGenerator;
108 
112  private $attributeCollectionFactory;
113 
117  private $configurableProductGenerator;
118 
122  private $productCollectionFactory;
123 
127  private $productStartIndex;
128 
132  private $productsAmountProvider;
133 
137  private $categoryResolver;
138 
142  private $websiteCategoryProvider;
143 
147  private $priceProvider;
148 
152  private $swatchesGenerator;
153 
157  private $serializer;
158 
175  public function __construct(
177  \Magento\Setup\Fixtures\AttributeSet\AttributeSetFixture $attributeSetsFixture,
178  \Magento\Setup\Fixtures\AttributeSet\Pattern $attributePattern,
179  ProductGenerator $productGenerator,
180  CollectionFactory $attributeCollectionFactory,
181  ConfigurableProductGenerator $configurableProductGenerator,
182  ProductCollectionFactory $collectionFactory,
183  ProductsAmountProvider $productsAmountProvider,
184  CategoryResolver $categoryResolver,
185  WebsiteCategoryProvider $websiteCategoryProvider,
186  PriceProvider $priceProvider,
187  \Magento\Setup\Fixtures\AttributeSet\SwatchesGenerator $swatchesGenerator,
188  \Magento\Framework\Serialize\SerializerInterface $serializer
189  ) {
190  parent::__construct($fixtureModel);
191  $this->attributeSetsFixture = $attributeSetsFixture;
192  $this->attributePattern = $attributePattern;
193  $this->productGenerator = $productGenerator;
194  $this->attributeCollectionFactory = $attributeCollectionFactory;
195  $this->configurableProductGenerator = $configurableProductGenerator;
196  $this->productCollectionFactory = $collectionFactory;
197  $this->productsAmountProvider = $productsAmountProvider;
198  $this->categoryResolver = $categoryResolver;
199  $this->websiteCategoryProvider = $websiteCategoryProvider;
200  $this->priceProvider = $priceProvider;
201  $this->swatchesGenerator = $swatchesGenerator;
202  $this->serializer = $serializer;
203  }
204 
209  public function execute()
210  {
211  if (!$this->fixtureModel->getValue('configurable_products', [])) {
212  return;
213  }
214  $simpleProductsCount = $this->fixtureModel->getValue('simple_products', 0);
215  $maxAmountOfWordsDescription = $this->getSearchConfigValue('max_amount_of_words_description');
216  $maxAmountOfWordsShortDescription = $this->getSearchConfigValue('max_amount_of_words_short_description');
217  $minAmountOfWordsDescription = $this->getSearchConfigValue('min_amount_of_words_description');
218  $minAmountOfWordsShortDescription = $this->getSearchConfigValue('min_amount_of_words_short_description');
219 
220  foreach ($this->getConfigurableProductConfig() as $configurableConfig) {
221  $configurableSku = $configurableConfig['sku'];
222  $productAmount = $this->productsAmountProvider->getAmount(
223  $configurableConfig['products'],
224  $configurableSku
225  );
226  if (!$productAmount) {
227  continue;
228  }
229  $searchTerms = $this->getSearchTerms();
230  $shortDescriptionClosure = $this->getDescriptionClosure(
231  $searchTerms,
232  $simpleProductsCount,
233  $productAmount,
234  $maxAmountOfWordsShortDescription,
235  $minAmountOfWordsShortDescription,
236  'shortDescription'
237  );
238  $descriptionClosure = $this->getDescriptionClosure(
239  $searchTerms,
240  $simpleProductsCount,
241  $productAmount,
242  $maxAmountOfWordsDescription,
243  $minAmountOfWordsDescription,
244  'description'
245  );
246  $variationCount = $configurableConfig['variationCount'];
247  $attributeSet = $configurableConfig['attributeSet'];
248  $variationSkuClosure = function ($productId, $entityNumber) use ($configurableSku, $variationCount) {
249  $variationIndex = $this->getConfigurableVariationIndex($entityNumber, $variationCount);
250  $productId = $this->getConfigurableProductIndex($entityNumber, $variationCount);
251 
252  return sprintf($this->getConfigurableOptionSkuPattern($configurableSku), $productId, $variationIndex);
253  };
254  $fixture = [
255  'name' => $variationSkuClosure,
256  'sku' => $variationSkuClosure,
257  'price' => function ($index, $entityNumber) {
258  return $this->priceProvider->getPrice($entityNumber);
259  },
260  'website_ids' => function ($index, $entityNumber) use ($variationCount) {
261  $configurableIndex = $this->getConfigurableProductIndex($entityNumber, $variationCount);
262 
263  return $this->websiteCategoryProvider->getWebsiteIds($configurableIndex);
264  },
265  'attribute_set_id' => $attributeSet['id'],
266  'additional_attributes' => $this->getAdditionalAttributesClosure(
267  $attributeSet['attributes'],
268  $variationCount
269  ),
270  'visibility' => Visibility::VISIBILITY_NOT_VISIBLE,
271  ];
272  $this->productGenerator->generate($productAmount * $variationCount, $fixture);
273 
274  $skuClosure = function ($productId, $entityNumber) use ($configurableSku) {
275  return sprintf($configurableSku, $entityNumber + $this->getNewProductStartIndex());
276  };
277  $fixture = [
278  '_variation_sku_pattern' => $this->getFirstVariationSkuPattern($configurableConfig),
279  '_attributes_count' => count($attributeSet['attributes']),
280  '_variation_count' => $variationCount,
281  '_attributes' => $attributeSet['attributes'],
282  'type_id' => Configurable::TYPE_CODE,
283  'name' => $skuClosure,
284  'sku' => $skuClosure,
285  'description' => $descriptionClosure,
286  'short_description' => $shortDescriptionClosure,
287  'attribute_set_id' => $attributeSet['id'],
288  'website_ids' => $this->getConfigurableWebsiteIdsClosure(),
289  'category_ids' => $configurableConfig['category'],
290  'meta_keyword' => $skuClosure,
291  'meta_title' => $skuClosure,
292  ];
293 
294  $this->configurableProductGenerator->generate($productAmount, $fixture);
295  }
296  }
297 
302  private function getConfigurableWebsiteIdsClosure()
303  {
304  return function ($index, $entityNumber) {
305  return $this->websiteCategoryProvider->getWebsiteIds($entityNumber + $this->getNewProductStartIndex());
306  };
307  }
308 
316  private function getDefaultAttributeSetsConfig(array $defaultAttributeSets, $configurableProductsCount)
317  {
318  $attributeSetClosure = function ($index) use ($defaultAttributeSets) {
319  $attributeSetAmount = count(array_keys($defaultAttributeSets));
320  mt_srand($index);
321 
322  return $attributeSetAmount > ($index - 1) % (int)$this->fixtureModel->getValue('categories', 30)
323  ? array_keys($defaultAttributeSets)[mt_rand(0, $attributeSetAmount - 1)]
324  : 'Default';
325  };
326  $productsPerSet = [];
327  for ($i = 1; $i <= $configurableProductsCount; $i++) {
328  $attributeSet = $attributeSetClosure($i);
329  if (!isset($productsPerSet[$attributeSet])) {
330  $productsPerSet[$attributeSet] = 0;
331  }
332  $productsPerSet[$attributeSet]++;
333  }
334  $configurableConfig = [];
335  foreach ($defaultAttributeSets as $attributeSetName => $attributeSet) {
336  $skuSuffix = $attributeSetName === 'Default' ? '' : ' - ' . $attributeSetName;
337  $configurableConfig[] = [
338  'attributeSet' => $attributeSetName,
339  'products' => $productsPerSet[$attributeSetName],
340  'sku' => 'Configurable Product %s' . $skuSuffix,
341  ];
342  }
343 
344  return $configurableConfig;
345  }
346 
354  private function getFirstVariationSkuPattern($configurableConfig)
355  {
356  $productIndex = $this->getConfigurableProductIndex(0, $configurableConfig['variationCount']);
357 
358  return sprintf($this->getConfigurableOptionSkuPattern($configurableConfig['sku']), $productIndex, 1);
359  }
360 
366  private function getNewProductStartIndex()
367  {
368  if (null === $this->productStartIndex) {
369  $this->productStartIndex = $this->productCollectionFactory->create()
370  ->addFieldToFilter('type_id', Configurable::TYPE_CODE)
371  ->getSize() + 1;
372  }
373 
374  return $this->productStartIndex;
375  }
376 
384  private function getConfigurableProductIndex($entityNumber, $variationCount)
385  {
386  return floor($entityNumber / $variationCount) + $this->getNewProductStartIndex();
387  }
388 
396  private function getConfigurableVariationIndex($entityNumber, $variationCount)
397  {
398  return $entityNumber % $variationCount + 1;
399  }
400 
404  public function getActionTitle()
405  {
406  return 'Generating configurable products';
407  }
408 
412  public function introduceParamLabels()
413  {
414  return [];
415  }
416 
421  public function printInfo(OutputInterface $output)
422  {
423  if (!$this->fixtureModel->getValue('configurable_products', [])) {
424  return;
425  }
426 
427  $configurableProductConfig = $this->prepareConfigurableConfig(
428  $this->getDefaultAttributeSetsWithAttributes()
429  );
430  $generalAmount = array_sum(array_column($configurableProductConfig, 'products'));
431 
432  $output->writeln(sprintf('<info> |- Configurable products: %s</info>', $generalAmount));
433  }
434 
441  private function getDefaultAttributeSetsWithAttributes()
442  {
443  $attributeSets = $this->fixtureModel->getValue('attribute_sets', null);
444  $attributeSetData = [];
445 
446  if ($attributeSets !== null && array_key_exists('attribute_set', $attributeSets)) {
447  foreach ($attributeSets['attribute_set'] as $attributeSet) {
448  $attributesData = array_key_exists(0, $attributeSet['attributes']['attribute'])
449  ? $attributeSet['attributes']['attribute'] : [$attributeSet['attributes']['attribute']];
450  $attributes = [];
451  foreach ($attributesData as $attributeData) {
452  $values = [];
453  $optionsData = array_key_exists(0, $attributeData['options']['option'])
454  ? $attributeData['options']['option'] : [$attributeData['options']['option']];
455  foreach ($optionsData as $optionData) {
456  $values[] = $optionData['label'];
457  }
458 
459  $attributes[] = ['name' => $attributeData['attribute_code'], 'values' => $values];
460  }
461  $attributeSetData[$attributeSet['name']] = [
462  'name' => $attributeSet['name'],
463  'attributes' => $attributes
464  ];
465  }
466  }
467 
468  $attributeOptions = range(1, $this->getConfigurableProductsVariationsValue());
469  array_walk(
470  $attributeOptions,
471  function (&$item, $key) {
472  $item = 'option ' . ($key + 1);
473  }
474  );
475  $attributeSetData['Default'] = [
476  'name' => 'Default',
477  'attributes' => [
478  [
479  'name' => 'configurable_variation',
480  'values' => $attributeOptions
481  ]
482  ]
483  ];
484 
485  return $attributeSetData;
486  }
487 
495  private function getConfigurableProductConfig()
496  {
497  $defaultAttributeSets = $this->getDefaultAttributeSetsWithAttributes();
498  $configurableProductConfig = $this->prepareConfigurableConfig($defaultAttributeSets);
499 
500  $configurableProductConfig = array_map(function ($config) {
501  return array_merge(
502  [
503  'attributeSet' => null,
504  'attributes' => null,
505  'options' => null,
506  'sku' => null,
507  'category' => null,
508  'swatches' => null,
509  ],
510  $config
511  );
512  }, $configurableProductConfig);
513 
514  $skuPull = [];
515  foreach ($configurableProductConfig as $i => &$config) {
516  $attributeSet = $config['attributeSet'];
517  $attributes = $config['attributes'];
518  $options = (int)$config['options'];
519  if ($attributeSet && isset($defaultAttributeSets[$attributeSet])) {
520  // process default attribute sets
521  $attributeSet = $defaultAttributeSets[$attributeSet];
522  $attributes = count($attributeSet['attributes']);
523  $options = count($attributeSet['attributes'][0]['values']);
524  } elseif (is_array($attributes)) {
525  $attributeSet = $this->getCustomAttributeSet($attributes);
526  $options = array_column($attributes, 'options');
527  $attributes = count($attributes);
528  } elseif ($attributes && $options) {
529  $attributes = (int)$attributes;
530  // convert attributes and options to array for process custom attribute set creation
531  $attributesData = array_map(function ($options) use ($config) {
532  return ['options' => $options, 'swatches' => $config['swatches']];
533  }, array_fill(0, $attributes, $options));
534 
535  $attributeSet = $this->getCustomAttributeSet($attributesData);
536  }
537 
538  // do not process if any required option is missed
539  if (count(array_filter([$attributeSet, $attributes, $options])) !== 3) {
540  unset($configurableProductConfig[$i]);
541  continue;
542  }
543 
544  $config['sku'] = $this->getConfigurableSkuPattern($config, $attributeSet['name']);
545  $config['category'] = $this->getConfigurableCategory($config);
546  $config['attributeSet'] = $this->convertAttributesToDBFormat($attributeSet);
547  $config['attributes'] = $attributes;
548  $config['options'] = $options;
549  $config['variationCount'] = is_array($options) ? array_product($options) : pow($options, $attributes);
550  $skuPull[] = $config['sku'];
551  }
552 
553  if (count($skuPull) !== count(array_unique($skuPull))) {
554  throw new ValidatorException(
555  __("The configurable product's SKU pattern must be unique in an attribute set.")
556  );
557  }
558 
559  return $configurableProductConfig;
560  }
561 
570  private function prepareConfigurableConfig($defaultAttributeSets)
571  {
572  $configurableConfigs = $this->fixtureModel->getValue('configurable_products', []);
573  $configurableConfigs = is_array($configurableConfigs) ? $configurableConfigs : (int)$configurableConfigs;
574  if (is_int($configurableConfigs)) {
575  $configurableConfigs = $this->getDefaultAttributeSetsConfig($defaultAttributeSets, $configurableConfigs);
576  } elseif (isset($configurableConfigs['config'])) {
577  if (!isset($configurableConfigs['config'][0])) {
578  // in case when one variation is passed
579  $configurableConfigs = [$configurableConfigs['config']];
580  } else {
581  $configurableConfigs = $configurableConfigs['config'];
582  }
583 
584  foreach ($configurableConfigs as &$config) {
585  if (isset($config['attributes']) && is_array($config['attributes'])) {
586  if (!isset($config['attributes']['attribute'][0])) {
587  $config['attributes'] = [$config['attributes']['attribute']];
588  } else {
589  $config['attributes'] = $config['attributes']['attribute'];
590  }
591  }
592  }
593  } else {
594  throw new ValidatorException(
595  __('The configurable product config is invalid. Verify the product and try again.')
596  );
597  }
598 
599  return $configurableConfigs;
600  }
601 
606  private function getConfigurableCategory($config)
607  {
608  if (isset($config['category'])) {
609  return function ($index, $entityNumber) use ($config) {
610  $websiteClosure = $this->getConfigurableWebsiteIdsClosure();
611  $websites = $websiteClosure($index, $entityNumber);
612 
613  return $this->categoryResolver->getCategory(
614  array_shift($websites),
615  $config['category']
616  );
617  };
618  } else {
619  return function ($index, $entityNumber) {
620  return $this->websiteCategoryProvider->getCategoryId($entityNumber);
621  };
622  }
623  }
624 
630  private function getConfigurableSkuPattern($config, $attributeSetName)
631  {
632  $sku = isset($config['sku']) ? $config['sku'] : null;
633  if (!$sku) {
634  $sku = 'Configurable Product ' . $attributeSetName . ' %s';
635  }
636  if (false === strpos($sku, '%s')) {
637  $sku .= ' %s';
638  }
639 
640  return $sku;
641  }
642 
649  private function getCustomAttributeSet(array $attributes)
650  {
651  $attributeSetHash = crc32($this->serializer->serialize($attributes));
652  $attributeSetName = sprintf('Dynamic Attribute Set %s', $attributeSetHash);
653 
654  $pattern = $this->attributePattern->generateAttributeSet(
655  $attributeSetName,
656  count($attributes),
657  array_column($attributes, 'options'),
658  function ($index, $attribute) use ($attributeSetName, $attributes, $attributeSetHash) {
659  $swatch = [];
660  $attributeCode = substr(
661  sprintf('ca_%s_%s', $index, $attributeSetHash),
662  0,
664  );
665  $data = [
666  'attribute_code' => $attributeCode,
667  'frontend_label' => 'Dynamic Attribute ' . $attributeCode,
668  ];
669 
670  if (isset($attributes[$index - 1]['swatches'])) {
671  $data['is_visible_in_advanced_search'] = 1;
672  $data['is_searchable'] = 1;
673  $data['is_filterable'] = 1;
674  $data['is_filterable_in_search'] = 1;
675  $data['used_in_product_listing'] = 1;
676 
677  $swatch = $this->swatchesGenerator->generateSwatchData(
678  (int) $attributes[$index-1]['options'],
679  $attributeSetName . $index,
680  $attributes[$index-1]['swatches']
681  );
682  }
683 
684  return array_replace_recursive(
685  $attribute,
686  $data,
687  $swatch
688  );
689  }
690  );
691 
692  return $this->attributeSetsFixture->createAttributeSet($pattern);
693  }
694 
698  private function getSearchConfig()
699  {
700  if (!$this->searchConfig) {
701  $this->searchConfig = $this->fixtureModel->getValue('search_config', null);
702  }
703  return $this->searchConfig;
704  }
705 
710  private function getSearchConfigValue($name)
711  {
712  return $this->getSearchConfig() === null
713  ? 0 : ($this->getSearchConfig()[$name] === null ? 0 : $this->getSearchConfig()[$name]);
714  }
715 
719  private function getSearchTerms()
720  {
721  $searchTerms = $this->fixtureModel->getValue('search_terms', null);
722  if ($searchTerms !== null) {
723  $searchTerms = array_key_exists(0, $searchTerms['search_term'])
724  ? $searchTerms['search_term'] : [$searchTerms['search_term']];
725  } else {
726  $searchTerms = [];
727  }
728  return $searchTerms;
729  }
730 
736  private function getConfigurableProductsVariationsValue()
737  {
738  return $this->fixtureModel->getValue('configurable_products_variation', 3);
739  }
740 
749  private function getAdditionalAttributesClosure(array $attributes, $variationCount)
750  {
751  $optionsPerAttribute = array_map(function ($attr) {
752  return count($attr['values']);
753  }, $attributes);
754  $variationsMatrix = $this->generateVariationsMatrix(count($attributes), $optionsPerAttribute);
755 
756  return function ($attributeSetId, $index, $entityNumber) use ($attributes, $variationCount, $variationsMatrix) {
757  $variationIndex = $this->getConfigurableVariationIndex($entityNumber, $variationCount) - 1;
758  if (isset($variationsMatrix[$variationIndex])) {
759  $tempProductData = [];
760  foreach ($variationsMatrix[$variationIndex] as $attributeIndex => $optionIndex) {
761  $attributeCode = $attributes[$attributeIndex]['name'];
762  $option = $attributes[$attributeIndex]['values'][$optionIndex];
763  $tempProductData[$attributeCode] = $option;
764  }
765  return $tempProductData;
766  }
767 
768  return [];
769  };
770  }
771 
778  private function generateVariationsMatrix($attributesPerSet, $optionsPerAttribute)
779  {
780  $variationsMatrix = null;
781  for ($i = 0; $i < $attributesPerSet; $i++) {
782  $variationsMatrix[] = range(0, $optionsPerAttribute[$i] - 1);
783  }
784  return $this->generateVariations($variationsMatrix);
785  }
786 
792  private function generateVariations($variationsMatrix)
793  {
794  if (!$variationsMatrix) {
795  return [[]];
796  }
797  $set = array_shift($variationsMatrix);
798  $subset = $this->generateVariations($variationsMatrix);
799  $result = [];
800  foreach ($set as $value) {
801  foreach ($subset as $subValue) {
802  array_unshift($subValue, $value);
803  $result[] = $subValue;
804  }
805  }
806  return $result;
807  }
808 
815  private function getConfigurableOptionSkuPattern($skuPattern)
816  {
817  return $skuPattern . ' - option %s';
818  }
819 
829  private function getDescriptionClosure(
830  $searchTerms,
831  $simpleProductsCount,
832  $configurableProductsCount,
833  $maxAmountOfWordsDescription,
834  $minAmountOfWordsDescription,
835  $descriptionPrefix = 'description'
836  ) {
837  if (null === $this->dataGenerator) {
838  $fileName = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'dictionary.csv';
839  $this->dataGenerator = new DataGenerator(realpath($fileName));
840  }
841 
842  return function ($index) use (
843  $searchTerms,
844  $simpleProductsCount,
845  $configurableProductsCount,
846  $maxAmountOfWordsDescription,
847  $minAmountOfWordsDescription,
848  $descriptionPrefix
849  ) {
850  $countSearchTerms = is_array($searchTerms) ? count($searchTerms) : 0;
851  $count = !$searchTerms
852  ? 0
853  : round(
854  $searchTerms[$index % $countSearchTerms]['count'] * (
855  $configurableProductsCount / ($simpleProductsCount + $configurableProductsCount)
856  )
857  );
858  mt_srand($index);
859  return $this->dataGenerator->generate(
860  $minAmountOfWordsDescription,
861  $maxAmountOfWordsDescription,
862  $descriptionPrefix . '-' . $index
863  ) .
864  ($index <= ($count * $countSearchTerms) ? ' ' .
865  $searchTerms[$index % $countSearchTerms]['term'] : '');
866  };
867  }
868 
875  private function convertAttributesToDBFormat(array $attributeSet)
876  {
877  $attributeSetName = $attributeSet['name'];
878  $attributeSetId = null;
879  $attributes = [];
880  foreach ($attributeSet['attributes'] as $attributeData) {
881  $attributeCollection = $this->attributeCollectionFactory->create();
882 
883  $attributeCollection->setAttributeSetFilterBySetName($attributeSetName, Product::ENTITY);
884  $attributeCollection->addFieldToFilter(
885  'attribute_code',
886  $attributeData['name']
887  );
889  foreach ($attributeCollection as $attribute) {
890  $attributeSetId = $attribute->getAttributeSetId();
891  $values = [];
892  $options = $attribute->getOptions();
893  foreach ($options ?: [] as $option) {
894  if ($option->getValue()) {
895  $values[] = $option->getValue();
896  }
897  }
898  $attributes[] =
899  [
900  'name' => $attribute->getAttributeCode(),
901  'id' => $attribute->getAttributeId(),
902  'values' => $values
903  ];
904  }
905  }
906 
907  return ['id' => $attributeSetId, 'name' => $attributeSetName, 'attributes' => $attributes];
908  }
909 }
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$attr
Definition: text.phtml:8
$optionData
$pattern
Definition: website.php:22
$config
Definition: fraud_order.php:17
defined('TESTS_BP')||define('TESTS_BP' __DIR__
Definition: _bootstrap.php:60
$count
Definition: recent.phtml:13
$values
Definition: options.phtml:88
__()
Definition: __.php:13
$fileName
Definition: translate.phtml:15
$attributesData
$value
Definition: gender.phtml:16
$attributeCode
Definition: extend.phtml:12
$attributes
Definition: matrix.phtml:13
__construct(FixtureModel $fixtureModel, \Magento\Setup\Fixtures\AttributeSet\AttributeSetFixture $attributeSetsFixture, \Magento\Setup\Fixtures\AttributeSet\Pattern $attributePattern, ProductGenerator $productGenerator, CollectionFactory $attributeCollectionFactory, ConfigurableProductGenerator $configurableProductGenerator, ProductCollectionFactory $collectionFactory, ProductsAmountProvider $productsAmountProvider, CategoryResolver $categoryResolver, WebsiteCategoryProvider $websiteCategoryProvider, PriceProvider $priceProvider, \Magento\Setup\Fixtures\AttributeSet\SwatchesGenerator $swatchesGenerator, \Magento\Framework\Serialize\SerializerInterface $serializer)
$i
Definition: gallery.phtml:31
$index
Definition: list.phtml:44
if(!isset($_GET['name'])) $name
Definition: log.php:14