/** * 2015-11-28 { domain: "usageLimits", reason: "accessNotConfigured", message: "Access Not Configured. The API (Google Fonts Developer API) is not enabled for your project. Please use the Google Developers Console to update your configuration.", extendedHelp: "https://console.developers.google.com" } * @return array(string => string) */ private function firstError() { if (!isset($this->{__METHOD__})) { $this->{__METHOD__} = df_first($this['errors']); } return $this->{__METHOD__}; }
/** * 2016-09-04 * @override * @see \Df\Framework\Form\Element\Select::getValue() * @used-by \Df\Framework\Form\Element\Select2::setRenderer() * @return string|null */ public function getValue() { /** @var string|null $result */ $result = parent::getValue(); /** @var string[] $filter */ $filter = $this->filter(); return $filter && (!$result || !in_array($result, $filter)) ? df_first($filter) : ($result ?: self::$ORDER); }
/** * 2015-10-10 * @param string $name * @param string $label * @return void */ protected final function attribute($name, $label) { /** @var int $ordering */ static $ordering = 1000; df_eav_setup()->addAttribute('customer', $name, ['type' => 'static', 'label' => "{$this->labelPrefix()} {$label}", 'input' => 'text', 'sort_order' => $ordering, 'position' => $ordering++, 'visible' => false, 'system' => false, 'required' => false]); /** @var int $attributeId */ $attributeId = df_first(df_fetch_col('eav_attribute', 'attribute_id', 'attribute_code', $name)); df_conn()->insert(df_table('customer_form_attribute'), ['form_code' => 'adminhtml_customer', 'attribute_id' => $attributeId]); }
/** * 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; }
/** * 2015-09-27 * Цель метода — получение информации о формировании в данный момент заголовка страницы. * @uses \Magento\Framework\View\Page\Title::get() * @param Sb $sb * @param \Closure $proceed * @return string */ public function aroundGet(Sb $sb, \Closure $proceed) { df_state()->renderingTitle(true); try { $result = $proceed(); /** * Делаем браузерные заголовки административной части * более короткими и понятными: оставляем лишь первую и последнюю части заголовка. */ if (df_is_backend()) { /** @var string[] $resultA */ $resultA = explode(Sb::TITLE_GLUE, $result); $result = 3 > count($resultA) ? $result : implode(Sb::TITLE_GLUE, [df_first($resultA), df_last($resultA)]); } } finally { df_state()->renderingTitle(false); } 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__); }
/** * @param string $name * @return Entity|null */ public function findByName($name) { $this->getItems(); return df_first($this->findByNameAll($name)); }
/** * @param string $key * @param \\Zend_Validate_Interface|\Df\Zf\Validate\Type|string|mixed[] $validator * @param bool|null $isRequired [optional] * @throws \Df\Core\Exception * @return $this */ protected function _prop($key, $validator, $isRequired = null) { /** * Полезная проверка! * Как-то раз ошибочно описал поле без значения: private static $P__TYPE; * И при вызове $this->_prop(self::$P__TYPE, DF_V_STRING_NE) * получил диагностическое сообщение: «значение «» недопустимо для свойства «».» */ df_param_string_not_empty($key, 0); /** * Обратите внимание, что если метод @see _prop() был вызван с двумя параметрами, * то и count($arguments) вернёт 2, * хотя в методе @see _prop() всегда доступен и 3-х параметр: $isRequired. * Другими словами, @see func_get_args() не возвращает параметры по умолчанию, * если они не были реально указаны при вызове текущего метода. */ /** * Хотя документация к PHP говорит, * что @uses func_num_args() быть параметром других функций лишь с версии 5.3 PHP, * однако на самом деле @uses func_num_args() быть параметром других функций * в любых версиях PHP 5 и даже PHP 4. * http://3v4l.org/HKFP7 * http://php.net/manual/function.func-num-args.php */ if (2 < func_num_args()) { /** @var mixed[] $arguments */ $arguments = func_get_args(); $isRequired = df_last($arguments); /** @var bool $hasRequiredFlag */ $hasRequiredFlag = is_bool($isRequired) || is_null($isRequired); if ($hasRequiredFlag) { $validator = array_slice($arguments, 1, -1); } else { $isRequired = null; $validator = df_tail($arguments); } } /** @var \Zend_Validate_Interface[] $additionalValidators */ $additionalValidators = []; /** @var \Zend_Filter_Interface[] $additionalFilters */ $additionalFilters = []; if (!is_array($validator)) { $validator = Validator::resolveForProperty($this, $validator, $key, $skipOnNull = false === $isRequired); df_assert($validator instanceof \Zend_Validate_Interface); } else { /** @var array(\Zend_Validate_Interface|Df_Zf_Validate_Type|string) $additionalValidatorsRaw */ $additionalValidatorsRaw = df_tail($validator); $validator = Validator::resolveForProperty($this, df_first($validator), $key, $skipOnNull = false === $isRequired); df_assert($validator instanceof \Zend_Validate_Interface); foreach ($additionalValidatorsRaw as $additionalValidatorRaw) { /** @var \Zend_Validate_Interface|\Zend_Filter_Interface|string $additionalValidatorsRaw */ /** @var \Zend_Validate_Interface|\Zend_Filter_Interface $additionalValidator */ $additionalValidator = Validator::resolveForProperty($this, $additionalValidatorRaw, $key); if ($additionalValidator instanceof \Zend_Validate_Interface) { $additionalValidators[] = $additionalValidator; } if ($additionalValidator instanceof \Zend_Filter_Interface) { $additionalFilters[] = $additionalValidator; } } } $this->_validators[$key][] = $validator; if ($validator instanceof \Zend_Filter_Interface) { /** @var \Zend_Filter_Interface $filter */ $filter = $validator; $this->_addFilter($key, $filter); } foreach ($additionalFilters as $additionalFilter) { /** @var \Zend_Filter_Interface $additionalFilter */ $this->_addFilter($key, $additionalFilter); } /** * Раньше мы проводили валидацию лишь при извлечении значения свойства, * в методе @see getData(). * Однако затем мы сделали улучшение: * перенести валидацию на более раннюю стадию — инициализацию свойства @see setData(), * и инициализацию валидатора/фильтра @see _prop(). * Это улучшило диагностику случаев установки объекту некорректных значений свойств, * потому что теперь мы возбуждаем исключительную ситуацию * сразу при попытке установки некорректного значения. * А раньше, когда мы проводили валидацию лишь при извлечении значения свойства, * то при диагностике было не вполне понятно, * когда конкретно объекту было присвоено некорректное значение свойства. */ /** @var bool $hasValueVorTheKey */ $hasValueVorTheKey = array_key_exists($key, $this->_data); if ($hasValueVorTheKey) { Validator::checkProperty($this, $key, $this->_data[$key], $validator); } foreach ($additionalValidators as $additionalValidator) { /** @var \Zend_Validate_Interface $additionalValidator */ $this->_validators[$key][] = $additionalValidator; if ($hasValueVorTheKey) { Validator::checkProperty($this, $key, $this->_data[$key], $additionalValidator); } } return $this; }
/** * 2015-08-23 * Обратите внимание, что метод * @see Varien_Db_Adapter_Pdo_Mysql::getPrimaryKeyName() * возвращает не название колонки, а слово «PRIMARY», * поэтому он нам не подходит. * @used-by Df_Localization_Onetime_Dictionary_Db_Table::primaryKey() * @param string $table * @return string|null */ function df_primary_key($table) { return dfcf(function ($table) { return df_first(df_nta(dfa_deep(df_conn()->getIndexList($table), 'PRIMARY/COLUMNS_LIST'))); }, func_get_args()); }
/** * 2016-08-24 * Несмотря на то, что опция @see \Df\Payment\Settings::askForBillingAddress() * стала общей для всех моих платёжных модулей, * платёжный адрес у заказа всегда присутствует, * просто при askForBillingAddress = false платёжный адрес является вырожденным: * он содержит только email покупателя. * * Только что проверил, как метод работает для анонимных покупателей. * Оказывается, если аноничный покупатель при оформлении заказа указал адреса, * то эти адреса в данном методе уже будут доступны как посредством * @see \Magento\Sales\Model\Order::getAddresses() * так и, соответственно, посредством @uses \Magento\Sales\Model\Order::getBillingAddress() * и @uses \Magento\Sales\Model\Order::getShippingAddress() * Так происходит в связи с особенностью реализации метода * @see \Magento\Sales\Model\Order::getAddresses() * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Sales/Model/Order.php#L1957-L1969 if ($this->getData('addresses') == null) { $this->setData('addresses', $this->getAddressesCollection()->getItems()); } return $this->getData('addresses'); * Как видно, метод необязательно получает адреса из базы данных: * для анонимных покупателей (или ранее покупавших, но указавшим в этот раз новый адрес), * адреса берутся из поля «addresses». * А содержимое этого поля устанавливается методом @see \Magento\Sales\Model\Order::addAddress() * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Sales/Model/Order.php#L1238-L1250 * * @param bool $bs * @return OA */ private function addressMixed($bs) { return dfc($this, function ($bs) { /** @var OA[] $aa */ $aa = df_clean([$this->addressB(), $this->addressS()]); $aa = $bs ? $aa : array_reverse($aa); /** @var OA $result */ $result = df_create(OA::class, df_clean(df_first($aa)->getData()) + df_last($aa)->getData()); /** * 2016-08-24 * Сам класс @see \Magento\Sales\Model\Order\Address никак order не использует. * Однако пользователи класса могут ожидать работоспособность метода * @see \Magento\Sales\Model\Order\Address::getOrder() * В частности, этого ожидает метод @see \Dfe\TwoCheckout\Address::build() */ $result->setOrder($this->o()); return $result; }, func_get_args()); }
/** * 2016-08-08 * @used-by \Df\Payment\Charge::iia() * @used-by \Df\Payment\Method::iia() * @param II|OP|QP $payment * @param string|string[]|null $keys [optional] * @return mixed|array(string => mixed) */ function dfp_iia(II $payment, $keys = null) { /** @var mixed|array(string => mixed) $result */ if (is_null($keys)) { $result = $payment->getAdditionalInformation(); } else { if (!is_array($keys)) { $keys = df_tail(func_get_args()); } $result = 1 === count($keys) ? $payment->getAdditionalInformation(df_first($keys)) : dfa_select_ordered($payment->getAdditionalInformation(), $keys); } return $result; }
/** * 2015-12-28 * Преобразует при необходимости простой одномерный массив в список опций. * @param string[] $values * @return array(array(string => string|int)) */ function df_a_to_options(array $values) { /** @var mixed $first */ $first = df_first($values); return is_null($first) || isset($first['value']) ? $values : df_map_to_options(dfa_combine_self($values)); }
/** * @param string|mixed[] $pattern * @return string * @throws \Exception */ function df_sprintf_strict($pattern) { /** @var mixed[] $arguments */ if (is_array($pattern)) { $arguments = $pattern; $pattern = df_first($arguments); } else { $arguments = func_get_args(); } /** @var string $result */ if (1 === count($arguments)) { $result = $pattern; } else { try { $result = vsprintf($pattern, df_tail($arguments)); } catch (Exception $e) { /** @var bool $inProcess */ static $inProcess = false; if (!$inProcess) { $inProcess = true; df_error('При выполнении sprintf произошёл сбой «{message}».' . "\nШаблон: {$pattern}." . "\nПараметры:\n{params}.", ['{message}' => df_ets($e), '{params}' => print_r(df_tail($arguments), true)]); $inProcess = false; } } } return $result; }
/** * Возвращает неиспользуемое имя файла в заданной папке $directory по заданному шаблону $template. * Результатом всегда является непустая строка. * @param string $directory * @param string $template * @param string $ds [optional] * @return string */ function df_file_name($directory, $template, $ds = '-') { // 2016-11-09 // Отныне $template может содержать файловый путь: // в этом случае этот файловый путь убираем из $template и добавляем к $directory. $directory = df_path_n($directory); $template = df_path_n($template); if (df_contains($template, '/')) { /** @var string $templateA */ $templateA = explode('/', $template); $template = array_pop($templateA); $directory = df_cc_path($directory, $templateA); } /** @var string $result */ /** @var int $counter */ $counter = 1; /** @var bool $hasOrderingPosition */ $hasOrderingPosition = df_contains($template, '{ordering}'); /** @var \Zend_Date $now */ $now = \Zend_Date::now()->setTimezone('Europe/Moscow'); /** @var array(string => string) */ $vars = df_map_k(function ($k, $v) use($ds, $now) { return df_dts($now, implode($ds, $v)); }, ['date' => ['y', 'MM', 'dd'], 'time' => ['HH', 'mm'], 'time-full' => ['HH', 'mm', 'ss']]); /** * 2016-11-09 * @see \Zend_Date неправильно работает с миллисекундами: * всегда возвращает 0 вместо реального количества миллисекунд. * Так происходит из-за дефекта в методах * @see \Zend_Date::addMilliSecond() * @see \Zend_Date::setMilliSecond() * Там такой код: list($milli, $time) = explode(" ", microtime()); $milli = intval($milli); * https://github.com/OpenMage/magento-mirror/blob/1.9.3.0/lib/Zend/Date.php#L4490-L4491 * Этот код ошибочен, потому что после первой операции * $milli содержит дробное значение меньше 1, например: 0.653... * А вторая операция тупо делает из этого значения 0. */ $vars['time-full-ms'] = implode($ds, [$vars['time-full'], sprintf('%02d', round(100 * df_first(explode(' ', microtime()))))]); while (true) { /** @var string $fileName */ $fileName = df_var($template, ['ordering' => sprintf('%03d', $counter)] + $vars); /** @var string $fileFullPath */ $fileFullPath = $directory . DS . $fileName; if (!file_exists($fileFullPath)) { /** * Раньше здесь стояло file_put_contents, * и иногда почему-то возникал сбой: * failed to open stream: No such file or directory. * Может быть, такой сбой возникает, если папка не существует? */ $result = $fileFullPath; break; } else { if ($counter > 999) { df_error("Счётчик достиг предела ({$counter})."); } else { $counter++; /** * Если в шаблоне имени файла * нет переменной «{ordering}» — значит, надо добавить её, * чтобы в следующей интерации имя файла стало уникальным. * Вставляем «{ordering}» непосредственно перед расширением файла. * Например, rm.shipping.log преобразуем в rm.shipping-{ordering}.log */ if (!$hasOrderingPosition && 2 === $counter) { /** @var string[] $fileNameTemplateExploded */ $fileNameTemplateExploded = explode('.', $template); /** @var int $secondFromLastPartIndex*/ $secondFromLastPartIndex = max(0, count($fileNameTemplateExploded) - 2); /** @var string $secondFromLastPart */ $secondFromLastPart = dfa($fileNameTemplateExploded, $secondFromLastPartIndex); df_assert_string_not_empty($secondFromLastPart); $fileNameTemplateExploded[$secondFromLastPartIndex] = implode('--', [$secondFromLastPart, '{ordering}']); /** @var string $newFileNameTemplate */ $newFileNameTemplate = implode('.', $fileNameTemplateExploded); df_assert_ne($template, $newFileNameTemplate); $template = $newFileNameTemplate; } } } } return df_path_n($result); }
/** * 2016-05-21 * @param string[] ...$keys * @return mixed|array(string => mixed) */ protected function iia(...$keys) { return !$keys ? $this->ii()->getAdditionalInformation() : (1 === count($keys) ? $this->ii()->getAdditionalInformation(df_first($keys)) : dfa_select_ordered($this->ii()->getAdditionalInformation(), $keys)); }
/** @return string */ private function family() { return df_first($this->familyA()); }
/** @return string */ public function family() { return df_first($this->familyA()); }
/** * 2016-01-01 * 2016-10-20 * Нельзя делать параметр $c опциональным, потому что иначе получим сбой: * «get_class() called without object from outside a class» * https://3v4l.org/k6Hd5 * @param string|object $c * @return string */ function df_class_first($c) { return df_first(df_explode_class($c)); }