/** * @param int $levelsToSkip * Позволяет при записи стека вызовов пропустить несколько последних вызовов функций, * которые и так очевидны (например, вызов данной функции, вызов df_bt() и т.п.) * @return void */ function df_bt($levelsToSkip = 0) { /** @var array $bt */ $bt = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), $levelsToSkip); /** @var array $compactBT */ $compactBT = []; /** @var int $traceLength */ $traceLength = count($bt); /** * 2015-07-23 * 1) Удаляем часть файлового пути до корневой папки Magento. * 2) Заменяем разделитель папок на унифицированный. */ /** @var string $bp */ $bp = BP . DS; /** @var bool $nonStandardDS */ $nonStandardDS = DS !== '/'; for ($traceIndex = 0; $traceIndex < $traceLength; $traceIndex++) { /** @var array $currentState */ $currentState = dfa($bt, $traceIndex); /** @var array(string => string) $nextState */ $nextState = dfa($bt, 1 + $traceIndex, []); /** @var string $file */ $file = str_replace($bp, '', dfa($currentState, 'file')); if ($nonStandardDS) { $file = df_path_n($file); } $compactBT[] = ['File' => $file, 'Line' => dfa($currentState, 'line'), 'Caller' => !$nextState ? '' : df_cc_method($nextState), 'Callee' => !$currentState ? '' : df_cc_method($currentState)]; } df_report('bt-{date}-{time}.log', print_r($compactBT, true)); }
/** * 2016-01-29 * @param AE|E $e * @param string|null $key [optional] * @param string|null|callable $default [optional] * @return string|null|array(string => mixed) */ function df_fe_fc(AE $e, $key = null, $default = null) { /** @var array(string => mixed) $result */ $result = df_fe_top($e)->getFieldConfig(); df_assert_array($result); return $key ? dfa($result, $key, $default) : $result; }
/** * Создаёт объект-дату по строке вида «20131115153657». * @param string $timestamp * @param string|null $offsetType [optional] * @return ZD */ function df_date_from_timestamp_14($timestamp, $offsetType = null) { df_assert(ctype_digit($timestamp)); df_assert_eq(14, strlen($timestamp)); // Почему-то new Zend_Date($timestamp, 'yMMddHHmmss') у меня не работает /** @var string $pattern */ $pattern = '#(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})#'; /** @var int[] $matches */ $matches = []; /** @var int $r */ $r = preg_match($pattern, $timestamp, $matches); df_assert_eq(1, $r); /** @var int $hour */ $hour = df_nat0(dfa($matches, 4)); if ($offsetType) { df_assert_in($offsetType, ['UTC', 'GMT']); /** @var int $offsetFromGMT */ $offsetFromGMT = df_round(df_int(df_dts(ZD::now(), ZD::TIMEZONE_SECS)) / 3600); $hour += $offsetFromGMT; if ('UTC' === $offsetType) { $hour++; } } return new ZD(['year' => dfa($matches, 1), 'month' => dfa($matches, 2), 'day' => dfa($matches, 3), 'hour' => $hour, 'minute' => dfa($matches, 5), 'second' => dfa($matches, 6)]); }
/** * @param int $amount * @param array $forms * @return string */ public function getForm($amount, array $forms) { df_param_integer($amount, 0); /** @var string $result */ $result = dfa($forms, $this->getIndex($amount)); df_result_string($result); return $result; }
/** * 2016-08-02 * @param string $tabName * @param string $tabProperty * @return string|null */ public static function tab($tabName, $tabProperty) { /** @var Structure $s */ $s = df_config_structure(); if (!isset($s->_data['tabs'])) { $s->getTabs(); } return dfa(dfa($s->_data['tabs'], $tabName, []), $tabProperty); }
/** * 2015-11-29 * @param string $name * @return Variant * @throws \Exception */ public function variant($name) { /** @var Variant|null $result */ $result = dfa($this->variants(), $name); if (!$result) { throw new \Exception("Variant «{$name}» of font «{$this->family()}» is not found."); } return $result; }
/** * 2015-11-14 * Цель плагина — алфавитное упорядочивание моих модулей * в разделе административных настроек модулей. * @see \Magento\Config\Model\Config\Structure\Element\Iterator\Tab::setElements() * @param Sb $sb * @param array(string => array(string => string)) $elements * @param string $scope * @return array() */ public function beforeSetElements(Sb $sb, array $elements, $scope) { /** @var array(string => string)|null $sections */ $sections = dfa_deep($elements, '_df/children'); if ($sections) { uasort($sections, function ($a, $b) { return strcasecmp(dfa($a, 'label'), dfa($b, 'label')); }); $elements['_df']['children'] = $sections; } return [$elements, $scope]; }
/** * 2015-12-30 * @param string|null $key [optional] * @return ArrayItem|array(string => ArrayItem)|null */ public function get($key = null) { if (!isset($this->{__METHOD__})) { /** @var string $class */ $class = $this[self::$P__ITEM_CLASS]; $this->{__METHOD__} = df_index(function (ArrayItem $o) { return $o->getId(); }, array_map(function ($data) use($class) { return new $class($data); }, $this->a())); } return is_null($key) ? $this->{__METHOD__} : dfa($this->{__METHOD__}, $key); }
/** * 2016-05-13 * Наша проблема заключается в том, что Magento передаёт флаг $isMultiselect = true * только для элементов типа multiselect: * How is the isMultiselect parameter passed * to the toOptionArray method of @see \Magento\Framework\Data\OptionSourceInterface? * https://mage2.pro/t/1613 * Наш же элемент управления имеет другой тип: type='Df\Framework\Form\Element\Multiselect' * https://code.dmitry-fedyuk.com/m2e/stripe/blob/b105882/etc/adminhtml/system.xml#L250 * Получается, что флаг $isMultiselect имеет значение false, * и тогда метод @see \Magento\Directory\Model\Config\Source\Country::toOptionArray() * и другие аналогичные методы добавляют фэйковую опцию «--Please Select--». * Нам она не нужна, поэтому удаляем её здесь. * * @override * @see \Magento\Framework\DataObject::__call() * @used-by \Magento\Config\Block\System\Config\Form::_initElement() * https://github.com/magento/magento2/blob/ffea3cd/app/code/Magento/Config/Block/System/Config/Form.php#L375-L377 * How are the options set to a select/multiselect form element? https://mage2.pro/t/1615 * How is @see \Magento\Config\Model\Config\Structure\Element\Field::getOptions() * implemented and used? https://mage2.pro/t/1616 * @param array $values */ public function setValues(array $values) { /** @var array(string => string)|null $first */ $first = df_first($values); /** * 2016-05-13 * @see \Magento\Directory\Model\Config\Source\Country::toOptionArray() * https://github.com/magento/magento2/blob/ffea3cd/app/code/Magento/Directory/Model/Config/Source/Country.php#L51-L51 */ if ($first && (string) __('--Please Select--') === (string) dfa($first, 'label')) { array_shift($values); } $this['values'] = $values; }
/** * 2016-08-03 * Начиная с Magento 2.1.0 backend model создаётся только если данные присутствуют в базе данных * для конкретной области действия настроек (scope и scopeId). * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Config/Block/System/Config/Form.php#L309-L327 * Если данные отсутстствуют в БД для конкретной области действия настроек, * то backend model вообще не создаётся, * однако данные всё равно извлекаются из БД из общей области действия настроек: * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Config/Block/System/Config/Form.php#L323-L327 * Видимо, такое поведение дефектно: данные могут попасть в форму * в обход обработки и валидации их в backend model. * * Ранее (до версии 2.1.0) backend model создавалась в любом случае: * такое поведение я считаю более верным: * https://github.com/magento/magento2/blob/2.0.8/app/code/Magento/Config/Block/System/Config/Form.php#L330-L342 * * В плагин мы попадаем отсюда: @see \Magento\Config\Block\System\Config\Form::_initElement() * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Config/Block/System/Config/Form.php#L347-L367 * * @see \Magento\Framework\Data\Form\Element\Fieldset::addField() * @param Sb|El $sb * @param string $elementId * @param string $type * @param array(string => mixed) $config * @param bool $after [optional] * @param bool $isAdvanced [optional] * @return array(string|bool|array(string => mixed)) */ public function beforeAddField(Sb $sb, $elementId, $type, array $config, $after = false, $isAdvanced = false) { /** @var array(string => mixed)|null $fc */ $fc = dfa($config, 'field_config'); /** * 2016-09-27 * Модуль Unirgy Dropship ошибочно пихает в $fc объект класса stdClass вместо массива: * https://code.dmitry-fedyuk.com/m2e/stripe/issues/5 */ if ($fc && is_array($fc)) { /** @var string|null $path */ $path = df_cc_path(dfa($fc, 'path'), dfa($fc, 'id')); /** @var string|null $backendC */ $backendC = dfa($fc, 'backend_model'); /** * 2016-08-03 * Конкретное значение тега «type» из system.xml можно получить вызовом dfa($fc, 'type') * Однако нам это не нужно: достаточно использовать аргумент $type: * @see \Magento\Config\Model\Config\Structure\Element\Field::getType() */ /** @var mixed|null $value */ $value = dfa($config, 'value'); if ($path && $backendC && !B::isProcessed($path) && is_a($type, El::class, true) && !is_null($value)) { /** * 2016-08-03 * По аналогии с @see \Magento\Config\Block\System\Config\Form::_initElement() * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Config/Block/System/Config/Form.php#L314-L320 */ /** @var B $b */ $b = df_create($backendC); $b->setPath($path); $b->setValue($value); /** * 2016-08-03 * По аналогии с @see \Magento\Config\Block\System\Config\Form::getWebsiteCode() */ $b->setWebsite(df_request('website', '')); /** * 2016-08-03 * По аналогии с @see \Magento\Config\Block\System\Config\Form::getStoreCode() */ $b->setStore(df_request('store', '')); $b->afterLoad(); $config['value'] = $b->getValue(); } } return [$elementId, $type, $config, $after, $isAdvanced]; }
/** * 2015-12-14 * Добавил возможность передачи в качестве первого параметра @see O * причём как в виде объекта, так и строки-класса. * * Такая возможность позволяет нам эффективно рендерить шаблоны без иерархии своих классов-блоков. * В Российской сборке для Magento 1.x * нам приходилось дублировать один и тот же код в классе базовой модели (аналоге класса O), * и в 2-х базовых классах блоков (абстрактном и блоке с шаблоном), т.е. в 3 местах. * Теперь же нам этого делать не нужно. * * @used-by df_phtml() * @param string|O|null $type * @param string|array(string => mixed) $data [optional] * @param string|null $template [optional] * * 2016-11-22 * @param array $vars [optional] * Параметры $vars будут доступны в шаблоне в качестве переменных: * @see \Magento\Framework\View\TemplateEngine\Php::render() extract($dictionary, EXTR_SKIP); * https://github.com/magento/magento2/blob/2.1.2/lib/internal/Magento/Framework/View/TemplateEngine/Php.php#L58 * * @return AbstractBlock|BlockInterface|Template */ function df_block($type, $data = [], $template = null, array $vars = []) { /** * 2015-12-14 * $type может быть как объектом, так и строкой: * https://3v4l.org/udMMH */ /** @var O $context */ if (!is_a($type, O::class, true)) { $context = null; } else { $context = is_object($type) ? $type : new $type(); $type = null; } if (is_null($type)) { $type = df_is_backend() ? BackendTemplate::class : Template::class; } /** @var string|null $template */ if (is_string($data)) { $template = $data; $data = []; } /** @var AbstractBlock|BlockInterface|Template $result */ /** * 2016-11-22 * В отличие от Magento 1.x, в Magento 2 нам нужен синтаксис ['data' => $data]: * @see \Magento\Framework\View\Layout\Generator\Block::createBlock(): * $block->addData(isset($arguments['data']) ? $arguments['data'] : []); * https://github.com/magento/magento2/blob/2.1.2/lib/internal/Magento/Framework/View/Layout/Generator/Block.php#L240 * В Magento 1.x было не так: * https://github.com/OpenMage/magento-mirror/blob/1.9.3.1/app/code/core/Mage/Core/Model/Layout.php#L482-L491 */ $result = df_layout()->createBlock($type, dfa($data, 'name'), ['data' => $data]); // 2016-11-22 $result->assign($vars); if ($template && $result instanceof Template) { $result->setTemplate(df_append($template, '.phtml')); } if ($context) { // 2016-11-22 // «Sets the object that should represent $block in template.» $result->setTemplateContext($context); } return $result; }
/** * @used-by \Df\Qa\Method::raiseErrorParam() * @param int $paramOrdering zero-based * @return \ReflectionParameter */ public function methodParameter($paramOrdering) { df_param_integer($paramOrdering, 0); if (!isset($this->{__METHOD__}[$paramOrdering])) { // Метод должен существовать df_assert($this->method()); // Параметр должен существовать if ($paramOrdering >= count($this->method()->getParameters())) { df_error("Программист ошибочно пытается получить значение параметра с индексом {$paramOrdering}" . " метода «{$this->methodName()}», хотя этот метод принимает всего %d параметров.", count($this->method()->getParameters())); } df_assert_lt(count($this->method()->getParameters()), $paramOrdering); /** @var \ReflectionParameter $result */ $result = dfa($this->method()->getParameters(), $paramOrdering); df_assert($result instanceof \ReflectionParameter); $this->{__METHOD__}[$paramOrdering] = $result; } return $this->{__METHOD__}[$paramOrdering]; }
/** * Sets validator options * Accepts the following option keys: * 'min' => scalar, minimum border * 'max' => scalar, maximum border * 'inclusive' => boolean, inclusive border values * @override * @param array|\Zend_Config $options */ public function __construct(array $options) { /** * Спецификация конструктора класса Zend_Validate_Between * различается между Zend Framework 1.9.6 (Magento 1.4.0.1) * и Zend Framework 1.11.1 (Magento 1.5.0.1). * * Именно для устранения для пользователя данных различий * служит наш класс-посредник \Df\Zf\Validate\Between */ if (version_compare(\Zend_Version::VERSION, '1.10', '>=')) { /** @noinspection PhpParamsInspection */ parent::__construct($options); } else { /** @noinspection PhpParamsInspection */ parent::__construct(dfa($options, 'min'), dfa($options, 'max'), dfa($options, 'inclusive')); } }
/** * @param int|string $index * @param string|array(array(string => int|string|mixed[])) $option * @return string */ private function renderOption($index, $option) { /** @var string $result */ if (!is_array($option)) { // опция имеет формат array('RU' => 'Россия') $result = $this->renderOptionTag($index, $option); } else { /** @var int|string|array(string => string)|array(array(string => string|array(string => string))) $value */ $value = dfa($option, 'value'); /** @var string $label */ $label = dfa($option, 'label'); if (!is_array($value)) { // опция имеет формат array('label' => 'Россия', 'value' => 'RU') $result = $this->renderOptionTag($value, $label); } else { // опция имеет формат array('label' => 'группа опций', 'value' => array(...)) $result = df_tag('optgroup', ['label' => $label], $this->implodeTags($this->renderOptions($value))); } } return $result; }
/** * 2016-07-30 * @override * @see \Df\Framework\Form\Element\Text::onFormInitialized() * @used-by \Df\Framework\Plugin\Data\Form\Element\AbstractElement::afterSetForm() * @return void */ public function onFormInitialized() { parent::onFormInitialized(); $this->addClass('df-quantity'); /** @var string|null|Phrase $title */ $title = $this->getTitle(); $this->unsTitle(); /** @var Text|E $input */ $input = $this->text('value', $this->getLabel(), ['title' => $title]); $this->unsLabel(); /** @var array(int|string => string)|string $values */ $values = dfa($this->_data, self::P__VALUES, []); if (is_string($values)) { $values = [$values]; } $this->unsetData(self::P__VALUES); if (1 < count($values)) { $this->select('units', null, $values, ['title' => $title]); } else { $input->setAfterElementHtml(df_first($values)); } df_fe_init($this, __CLASS__); }
/** * 2016-08-09 * @used-by \Dfe\AllPay\Block\Info\BankCard::prepareDic() * @param string $nameToFind * @param string $name * @param string|Phrase $value * @param int $weight [optional] */ public function addAfter($nameToFind, $name, $value, $weight = 0) { /** @var Entry|null $itemToFind */ $itemToFind = dfa($this->_items, $nameToFind); $this->add($name, $value, !$itemToFind ? 0 : 1 + $itemToFind->weight()); }
/** * 2016-05-20 * Возвращает 2-буквенный код страны по стандарту ISO 3166-1 alpha-2 * по названию страны для заданной локали (или системной локали по умолчанию) * https://ru.wikipedia.org/wiki/ISO_3166-1 * @param string $name * @param string|null $locale [optional] * @return string|null */ function df_country_ntc($name, $locale = null) { df_param_string_not_empty($name, 0); return dfa(df_countries_ntc($locale), mb_strtoupper(df_trim($name))); }
/** * @param string[] $matches * @return string */ private static function nl2brCallback(array $matches) { return str_replace('{rm-newline}', '{rm-newline-preserve}', dfa($matches, 0, '')); }
/** * 2015-12-16 * @return string */ private function variant() { if (!isset($this->{__METHOD__})) { $this->{__METHOD__} = dfa($this->familyA(), 1, ''); } return $this->{__METHOD__}; }
/** * @param string $name * @return Entity[] */ public function findByNameAll($name) { $this->getItems(); /** @noinspection PhpParamsInspection */ return df_nta(dfa($this->_mapFromNameToEntity, $name)); }
/** * @param int $errorCode * @return string|null */ private function translateErrorCode($errorCode) { return dfa(self::getErrorCodeMap(), $errorCode); }
/** * 2016-09-05 * @param int|string $value * @param array(int|string => mixed) $map * @return int|string|mixed */ function dftr($value, array $map) { return dfa($map, $value, $value); }
/** * 2015-08-10 * В случае отсутствия значения в кэше возвращаем не null, а false * ради согласованности с долгосрочным кэшем. * @param string $key * @return mixed|bool */ public function ramGet($key) { return dfa($this->_ram, $key, false); }
/** * 2016-05-19 * Родительский метод зачем-то делает цикл про элементам коллекции. * А мы, по сути, берём реализацию из @see \Magento\Framework\Data\Collection::getItemById() * @override * @see \Magento\Directory\Model\ResourceModel\Country\Collection::getItemById() * @param string $idValue * @return Country|null */ public function getItemById($idValue) { $this->load(); return dfa($this->_items, $idValue); }
/** * @param resource $image * @param int[] $rgba * @return int */ protected function colorAllocateAlpha($image, array $rgba) { /** @var int|bool $result */ $result = imagecolorallocatealpha($image, $rgba[0], $rgba[1], $rgba[2], dfa($rgba, 3, 0)); df_assert(false !== $result); return $result; }
/** * @param $number * @param string $gender * @return string */ private static function getNum1E9($number, $gender) { /** @var array(int => string) $words */ static $words = ['миллион', 'миллиона', 'миллионов']; return $number < 1000000.0 ? self::getNum1E6($number, $gender) : df_cc_s(self::getNum1000((int) ($number / 1000000.0), self::GENDER__FEMALE), dfa($words, self::getNum125((int) ($number / 1000000.0))), self::getNum1E6($number % 1000000.0, $gender)); }
/** * @param string $resource * @return \Magento\Framework\View\Asset\File */ function df_asset_create($resource) { return !df_starts_with($resource, 'http') && !df_starts_with($resource, '//') ? df_asset()->createAsset($resource) : df_asset()->createRemoteAsset($resource, dfa(['css' => 'text/css', 'js' => 'application/javascript'], df_file_ext($resource))); }
/** * 2016-07-30 * Синтаксис вызова таков: self::fdCssClass($data, 'df-fe-money'); * В настоящее время нигде не используется. * @param array(string => mixed) $data * @param string $class * @return void */ private static function fdCssClass(&$data, $class) { $data[self::$FD__CSS_CLASS] = df_cc_s(dfa($data, self::$FD__CSS_CLASS), $class); }
/** * @param string $key * @return string|null */ private function r($key) { return dfa($this->responseA(), $key); }
/** * 2016-07-13 * @param T $t * @param string|null $key [optional] * @param mixed|null $default [optional] * @return array(string => mixed)|mixed */ function df_trans_raw_details(T $t, $key = null, $default = null) { /** @var array(string => mixed)|mixed $result */ $result = $t->getAdditionalInformation(T::RAW_DETAILS); return null === $key ? $result : dfa($result, $key, $default); }