/** * @param $s|null $string * @param bool $throw [optional] * @return mixed|bool|null * @throws Exception * Returns the value encoded in json in appropriate PHP type. * Values true, false and null are returned as TRUE, FALSE and NULL respectively. * NULL is returned if the json cannot be decoded * or if the encoded data is deeper than the recursion limit. * http://php.net/manual/function.json-decode.php */ function df_json_decode($s, $throw = true) { /** @var mixed|bool|null $result */ /** * 2015-12-19 * PHP 7.0.1 почему-то приводит к сбою при декодировании пустой строки: * «Decoding failed: Syntax error» */ if ('' === $s || is_null($s)) { $result = $s; } else { /** * 2016-10-30 * json_decode('7700000000000000000000000') возвращает 7.7E+24 * https://3v4l.org/NnUhk * http://stackoverflow.com/questions/28109419 * Такие длинные числоподобные строки используются как идентификаторы КЛАДР * (модулем доставки «Деловые Линии»), и поэтому их нельзя так корёжить. * Поэтому используем константу JSON_BIGINT_AS_STRING * https://3v4l.org/vvFaF */ $result = json_decode($s, true, 512, JSON_BIGINT_AS_STRING); /** * 2016-10-28 * json_encode(null) возвращает строку 'null', * а json_decode('null') возвращает null. * Добавил проверку для этой ситуации, чтобы не считать её сбоем. */ if (is_null($result) && 'null' !== $s && $throw) { df_assert_ne(JSON_ERROR_NONE, json_last_error()); df_error("Parsing a JSON document failed with the message «%s».\nThe document:\n{$s}", json_last_error_msg()); } } return $result; }
/** * 2015-12-30 * Унифицирует вызов калбэков: * позволяет в качестве $method передавать как строковое название метода, * так и анонимную функцию, которая в качестве аргумента получит $object. * https://3v4l.org/pPGtA * @param object|mixed $object * @param string|callable|\Closure $method * @param mixed[] $params [optional] * @return mixed */ function df_call($object, $method, $params = []) { /** @var mixed $result */ if (!is_string($method)) { // $method — инлайновая функция $result = call_user_func_array($method, array_merge([$object], $params)); } else { /** @var bool $functionExists */ $functionExists = function_exists($method); /** @var bool $methodExists */ $methodExists = is_callable([$object, $method]); /** @var mixed $callable */ if ($functionExists && !$methodExists) { $callable = $method; $params = array_merge([$object], $params); } else { if ($methodExists && !$functionExists) { $callable = [$object, $method]; } else { if (!$functionExists) { df_error("Unable to call «{$method}»."); } else { df_error("An ambiguous name: «{$method}»."); } } } $result = call_user_func_array($callable, $params); } return $result; }
/** * @param $value * @return array|bool */ private function _splitUtf8($value) { try { $utfChars = str_split(iconv('UTF-8', 'UTF-32BE', $value), 4); } catch (\Exception $e) { df_error('Система получила строку в кодировке, отличной от требуемой кодировки UTF-8: «%s».', $value); } array_walk($utfChars, create_function('&$char', '$char = iconv("UTF-32BE", "UTF-8", $char);')); return $utfChars; }
/** * @override * @param mixed $value * @throws \Zend_Filter_Exception * @return bool */ public function filter($value) { /** @var bool $result */ try { $result = df_bool($value); } catch (\Exception $e) { df_error(new \Zend_Filter_Exception($e->getMessage())); } return $result; }
/** * @override * @param mixed $value * @throws \Zend_Filter_Exception * @return int */ public function filter($value) { /** @var int $result */ try { $result = df_nat($value); } catch (\Exception $e) { df_error(new \Zend_Filter_Exception(df_ets($e))); } return $result; }
/** * @param string $dir * @throws \Df\Core\Exception */ private function mkdir($dir) { try { $r = mkdir($dir, 0777, $recursive = true); df_throw_last_error($r); } catch (\Exception $e) { /** @var bool $isPermissionDenied */ $isPermissionDenied = df_contains($e->getMessage(), 'Permission denied'); df_error($isPermissionDenied ? "Операционная система запретила интерпретатору PHP создание папки «{$dir}»." : "Не удалось создать папку «{$dir}»." . "\nДиагностическое сообщение интерпретатора PHP: «{$e->getMessage()}»."); } }
/** * @return int */ private function getCategoryId() { /** @var int $result */ $result = null; if (1 > count($this->getProduct()->getCategoryIds())) { df_error(sprintf('Привяжите товар «%s» («%s») хотя бы к одному товарному разделу', $this->getProduct()->getSku(), $this->getProduct()->getName())); } else { $result = df_array_first($this->getProduct()->getCategoryIds()); } df_result_integer($result); return $result; }
/** * @param string $datetime * @param bool $throw [optional] * @return ZD|null * @throws Exception */ function df_date_from_db($datetime, $throw = true) { df_param_string_not_empty($datetime, 0); /** @var ZD|null $result */ $result = null; if ($datetime) { try { $result = new ZD($datetime, ZD::ISO_8601); } catch (Exception $e) { if ($throw) { df_error($e); } } } 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]; }
/** * 2016-03-26 * @param string|M $model * Идентификатор необязательно является целым числом, * потому что объект может загружаться по нестандартному ключу * (с указанием этого ключа параметром $field). * Так же, и первичный ключ может не быть целым числом (например, при загрузке валют). * @param string|int $id * @param bool $throwOnAbsence [optional] * @param string|null $field [optional] * @return M|null */ function df_load($model, $id, $throwOnAbsence = true, $field = null) { df_assert($id); if (!is_null($field)) { df_param_string($field, 3); } /** @var M|null $result */ $result = is_string($model) ? df_om()->create($model) : $model; df_assert($result instanceof M); $result->load($id, $field); if (!$result->getId()) { if (!$throwOnAbsence) { $result = null; } else { df_error('A model of class «%s» with ID «%s» is absent.', get_class($result), $id); } } return $result; }
/** * По поводу удаления @see \Df\Config\A::FAKE: * 2016-07-30 * Наша задача: удаление из массива всех целочисленных ключей, кроме @see \Df\Config\A::FAKE * Целочисленные ключи — это идентификаторы строк. * Нам, в принципе, без разницы их значения, лишь бы они были уникальными. * Однако в клиетской части (JavaScript) нам удобно, чтобы они шли по порядку и без дыр. * Более того, в алгоритме метода @see \Df\Framework\Form\Element\ArrayT::onFormInitialized() * мы уже подразумеваем, что ключи именно такими и являются: идут с нуля и без пропусков. * * Оказывается, и ключ @see \Df\Config\A::FAKE нам уже не нужен. * Мы ведь его добавляли в скрипте https://github.com/mage2pro/core/tree/b1f6809/Framework/view/adminhtml/web/formElement/array/main.js#L131 * с такой целью: * «2015-12-30 * Сервер так устроен, что если для конкретного поля формы * не придут данные с сервер (а при отсутствии элементов они не придут), * то сервер не обновляет значение в базе данных для этого поля. * Это приводит к тому эффекту, что если удалить все элементы, то сервер не сохранит данные. * Чтобы этого избежать, при отсутствии элементов передаём на сервер фейковый.» * * Поэтому теперь уже его можно удалить. * * По поводу валидации: * 2016-08-02 * Частную валидацию объектов проводим обязательно до проверки объектов на уникальность, * потому что если данные объекты некорректны, * то проверка на уникальность может дать некорректные результаты * и даже привести к дополнителным сбоям. * * @override * @see \Df\Config\Backend\Serialized::processI() * @used-by \Df\Config\Backend\Serialized::processA() * @param array(array(string => mixed)) $a * @return array(array(string => mixed)) * @throws \Exception */ protected function processI(array $a) { /** @var array(array(string => mixed)) $a */ $a = array_values(dfa_unset($a, A::FAKE)); /** @var I[] $e */ $e = iterator_to_array(A::i($this->entityC(), $a)); /** @uses \Df\Config\ArrayItem::validate() */ df_each($e, 'validate'); /** @var int[]|string[] $repeated */ $repeated = dfa_repeated(dfa_ids($e)); if ($repeated) { df_error('The following values are not uniqie: %s.', df_csv_pretty($repeated)); } $e = df_usort($e, function (I $a, I $b) { return $a->sortWeight() - $b->sortWeight(); }); /** @uses \Df\Config\ArrayItem::getData() */ return df_each($e, 'getData'); }
/** * @overide * @return Df_YandexMarket_Model_System_Config_Backend_Currency */ protected function _beforeSave() { parent::_beforeSave(); if (!in_array($this->getValue(), Df_YandexMarket_Model_System_Config_Source_Currency::getAllowedCurrencies())) { /** @var string $currencyName */ $currencyName = $this->getValue(); try { /** @var Zend_Currency $result */ $currency = Mage::app()->getLocale()->currency($this->getValue()); $currencyName = $currency->getName(); } catch (Exception $e) { } df_error(sprintf('Яндекс.Маркет не допускает указанную Вами валюту «%s» в качестве основной валюты магазина.', $currencyName)); } else { /** @var Df_Admin_Model_Config_BackendChecker_CurrencyIsSupported $checker */ $checker = df_model(Df_Admin_Model_Config_BackendChecker_CurrencyIsSupported::getNameInMagentoFormat(), array(Df_Admin_Model_Config_BackendChecker_CurrencyIsSupported::PARAM__CURRENCY_CODE => $this->getValue(), Df_Admin_Model_Config_BackendChecker_CurrencyIsSupported::PARAM__BACKEND => $this)); df_assert($checker instanceof Df_Admin_Model_Config_BackendChecker_CurrencyIsSupported); $checker->check(); } return $this; }
/** * 2015-04-05 * @used-by resolve() * @param string $name * @param bool $skipOnNull [optional] * @return \Zend_Validate_Interface|\Zend_Filter_Interface */ private static function byName($name, $skipOnNull = false) { /** @var array(bool => array(string => \Zend_Validate_Interface)) */ static $cache; if (!isset($cache[$skipOnNull][$name])) { /** @var array(string => string) $map */ static $map; if (!$map) { /** @var string[] $entries */ $entries = [DF_F_TRIM, DF_V_ARRAY, DF_V_BOOL, DF_V_FLOAT, DF_V_INT, DF_V_ISO2, DF_V_NAT, DF_V_NAT0, DF_V_STRING, DF_V_STRING_NE]; $map = array_combine($entries, $entries); } /** @var \Zend_Validate_Interface|\Zend_Filter_Interface $result */ if (isset($map[$name])) { $result = new $map[$name](); } else { if (df_class_exists($name) || @interface_exists($name)) { $result = \Df\Zf\Validate\ClassT::i($name); } else { df_error("Система не смогла распознать валидатор «{$name}»."); } } $result->{self::$SKIP_ON_NULL} = $skipOnNull; $cache[$skipOnNull][$name] = $result; } return $cache[$skipOnNull][$name]; }
/** * 2016-07-12 * @param string $url * @param string|E $message [optional] * @return void * @throws E|LE */ function df_assert_https($url, $message = null) { if (df_enable_assertions() && !df_check_https($url)) { df_error($message ? $message : df_sprintf('The URL «%s» is invalid, because the system expects an URL which starts with «https://».', $url)); } }
/** * 2016-07-27 * @see df_should_not_be_here() * @param string $method * @return void * @throws DFE */ function df_not_implemented($method) { df_error("The method «{$method}» is not implemented yet."); }
/** * @param string $path * @param callable $castFunction * @param string $castName * @param bool $throw [optional] * @return mixed|null */ private function descendWithCast($path, callable $castFunction, $castName, $throw = false) { /** @var string|null $resultAsText */ $resultAsText = $this->descendS($path, $throw); /** @var mixed|null $result */ if (!df_null_or_empty_string($resultAsText)) { $result = call_user_func($castFunction, $resultAsText); } else { if ($throw) { df_error('В документе XML по пути «%s» требуется %s число, однако там пусто.', $castName, $path); } else { $result = null; } } return $result; }
/** * 2015-08-24 * @used-by df_xml_load_file() * @param string $message * @throws X */ function df_xml_throw_last($message) { /** @var LibXMLError[] LibXMLError */ $errors = libxml_get_errors(); /** @var string[] $messages */ $messages = array($message); foreach ($errors as $error) { /** @var LibXMLError $error */ $messages[] = sprintf("(%d, %d) %s", $error->line, $error->column, $error->message); } df_error($messages); }
/** * @param string|null $key * @param mixed $value * @param string[]|bool $wrapInCData [optional] * @return X */ private function importString($key, $value, $wrapInCData = []) { /** @var bool $wrapInCDataAll */ $wrapInCDataAll = is_array($wrapInCData) ? false : !!$wrapInCData; $wrapInCData = df_nta($wrapInCData); /** * null означает, что метод importString * не должен создавать дочерний тэг $key, * а должен добавить текст * в качестве единственного содержимого текущего тэга */ if (!is_null($key)) { df_param_string($key, 0); } /** @var string $keyAsString */ $keyAsString = is_null($key) ? $this->getName() : $key; /** * @var bool $valueIsString */ $valueIsString = is_string($value); /** @var string $valueAsString */ $valueAsString = null; try { $valueAsString = $valueIsString ? $value : df_string($value); } catch (E $e) { df_error("Не могу сконвертировать значение ключа «%s» в строку.\n%s", $keyAsString, df_ets($e)); } /** @var bool $needWrapInCData */ $needWrapInCData = $wrapInCDataAll; if ($valueIsString && $valueAsString) { /** * Поддержка синтаксиса array( 'Представление' => df_cdata( $this->getAddress()->format( Mage_Customer_Model_Attribute_Data::OUTPUT_FORMAT_TEXT ) ) ) * Обратите внимание, что проверка на синтаксис[[]] должна предшествовать * проверке на принадлежность ключа $keyAsString в массиве $wrapInCData, * потому что при соответствии синтаксису[[]] нам надо удалить из значения символы[[]]. * Обратите внимание, что нам нужно выполнить проверку на синтаксис df_cdata ([[]]) * даже при $wrapInCDataAll = true, потому что маркеры [[ и ]] из данных надо удалять. */ /** * Перед вызовом медленной функции @see preg_match * выполняем более быструю и простую проверку @see df_contains */ if (df_contains($valueAsString, '[[') && df_contains($valueAsString, ']]')) { /** @var string $pattern */ $pattern = "#\\[\\[([\\s\\S]*)\\]\\]#mu"; /** @var string[] $matches */ $matches = []; if (1 === preg_match($pattern, $valueAsString, $matches)) { $valueAsString = $matches[1]; $needWrapInCData = true; } } $needWrapInCData = $needWrapInCData || in_array($keyAsString, $wrapInCData); } /** @var X $result */ $result = $needWrapInCData ? is_null($key) ? $this->setCData($valueAsString) : $this->addChildText($keyAsString, $valueAsString) : (is_null($key) ? $this->setValue($valueAsString) : $this->addChild($keyAsString, $valueAsString)); df_assert($result instanceof X); return $result; }
/** * Иногда я для разработки использую заплатку ядра для xDebug — * отключаю set_error_handler для режима разработчика. * * Так вот, xDebug при обработке фатальных сбоев (в том числе и E_RECOVERABLE_ERROR), * выводит на экран диагностическое сообщение, и после этого останавливает работу интерпретатора. * * Конечно, если у нас сбой типов E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, * E_COMPILE_ERROR, E_COMPILE_WARNING, то и set_error_handler не поможет * (не обрабатывает эти типы сбоев, согласно официальной документации PHP). * * Однако сбои типа E_RECOVERABLE_ERROR обработик сбоев Magento, * установленный посредством set_error_handler, переводит в исключительние ситуации. * * xDebug же при E_RECOVERABLE_ERROR останавивает работу интерпретатора, что нехорошо. * * Поэтому для функций, которые могут привести к E_RECOVERABLE_ERROR, * пишем обёртки, которые вместо E_RECOVERABLE_ERROR возбуждают исключительную ситуацию. * Одна из таких функций — df_string. * * @param mixed $value * @return string */ function df_string($value) { if (is_object($value)) { /** * К сожалению, нельзя здесь для проверки публичности метода использовать @see is_callable(), * потому что наличие @see \Magento\Framework\DataObject::__call() * приводит к тому, что @see is_callable всегда возвращает true. * Обратите внимание, что @uses method_exists(), в отличие от @see is_callable(), * не гарантирует публичную доступность метода: * т.е. метод может у класса быть, но вызывать его всё равно извне класса нельзя, * потому что он имеет доступность private или protected. * Пока эта проблема никак не решена. */ if (!method_exists($value, '__toString')) { df_error('Программист ошибочно пытается трактовать объект класса %s как строку.', get_class($value)); } } else { if (is_array($value)) { df_error('Программист ошибочно пытается трактовать массив как строку.'); } } return strval($value); }
/** * @param string|string[]|array(string => string) $text * @param string $type [optional] * @return string|string[] */ public function quote($text, $type = self::QUOTE__RUSSIAN) { if ('"' === $type) { $type = self::QUOTE__DOUBLE; } else { if ("'" === $type) { $type = self::QUOTE__SINGLE; } } /** @var array(string => string[]) $quotesMap */ static $quotesMap = [self::QUOTE__DOUBLE => ['"', '"'], self::QUOTE__RUSSIAN => ['«', '»'], self::QUOTE__SINGLE => ['\'', '\'']]; /** @var string[] $quotes */ $quotes = dfa($quotesMap, $type); if (!is_array($quotes)) { df_error('Неизвестный тип кавычки «%s».', $type); } df_assert_array($quotes); /** * 2016-11-13 * Обратите внимание на красоту решения: мы «склеиваем кавычки», * используя в качестве промежуточного звена исходную строку. * @param string $text * @return string */ $f = function ($text) use($quotes) { return implode($text, $quotes); }; return !is_array($text) ? $f($text) : array_map($f, $text); }
/** * 2016-07-27 * Адрес приобретает тип, только когда используется при оформлении заказа. * Пока же адрес просто принадлежит покупателю * @see \Magento\Customer\Model\Data\Address * @see \Magento\Customer\Api\Data\AddressInterface * а не используется в контексте оформления заказа, то такой адрес ещё типа не имеет, * и в будущем, в зависимости от контекста, * может использоваться и как адрес доставки, и как платёжный адрес. * * @uses \Magento\Quote\Model\Quote\Address::getAddressType() * @uses \Magento\Customer\Model\Address::getAddressType() * @param AA|CA|QA|OA $a * @return bool */ function df_address_is_billing($a) { return $a instanceof AA ? AA::TYPE_BILLING === $a['address_type'] : ($a instanceof OA ? OA::TYPE_BILLING === $a->getAddressType() : df_error("Invalid address class: «%s».", get_class($a))); }
/** * 2016-11-15 * @param O|Q $oq * @return Currency */ function dfp_currency($oq) { return $oq instanceof O ? $oq->getOrderCurrency() : ($oq instanceof Q ? df_currency($oq->getQuoteCurrencyCode()) : df_error()); }
/** * Возвращает неиспользуемое имя файла в заданной папке $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); }
/** * @used-by cacheLoad() * @used-by cacheSave() * @return string */ private function cacheKeyPerStore() { if (!isset($this->{__METHOD__})) { if (!\Df\Core\State::s()->storeInitialized()) { df_error('При кэшировании в разрезе магазина для объекта класса «%s» произошёл сбой,' . ' потому что система ещё не инициализировала текущий магазин.', get_class($this)); } $this->{__METHOD__} = $this->cacheKeyGlobal() . '[' . df_store()->getCode() . ']'; } return $this->{__METHOD__}; }
/** * @used-by df_throw_last_error() * @return void * @throws Exception */ public static function throwLast() { df_assert(error_get_last()); df_error(self::i()->main()); }
/** * 2015-02-18 * По смыслу функция @see df_extend() аналогична методу @see \Magento\Framework\Simplexml\Element::extend() * и предназначена для слияния настроечных опций, * только, в отличие от @see \Magento\Framework\Simplexml\Element::extend(), * @see df_extend() сливает не XML, а ассоциативные массивы. * * Обратите внимание, что вместо @see df_extend() нельзя использовать ни * @see array_replace_recursive(), ни @see array_merge_recursive(), * ни тем более @see array_replace() и @see array_merge() * Нерекурсивные аналоги отметаются сразу, потому что не способны сливать вложенные структуры. * Но и стандартные рекурсивные функции тоже не подходят: * * 1) * array_merge_recursive(array('width' => 180), array('width' => 200)) * вернёт: array(array('width' => array(180, 200))) * http://php.net/manual/function.array-merge-recursive.php * Наша функция df_extend(array('width' => 180), array('width' => 200)) * вернёт array('width' => 200) * * 2) * array_replace_recursive(array('x' => array('A', 'B')), array('x' => 'C')) * вернёт: array('x' => array('С', 'B')) * http://php.net/manual/function.array-replace-recursive.php * Наша функция df_extend(array('x' => array('A', 'B')), array('x' => 'C')) * вернёт array('x' => 'C') * * @param array(string => mixed) $defaults * @param array(string => mixed) $newValues * @return array(string => mixed) */ function df_extend(array $defaults, array $newValues) { /** @var array(string => mixed) $result */ // Здесь ошибочно было бы $result = [], // потому что если ключ отсутствует в $newValues, // то тогда он не попадёт в $result. $result = $defaults; foreach ($newValues as $key => $newValue) { /** @var int|string $key */ /** @var mixed $newValue */ /** @var mixed $defaultValue */ $defaultValue = dfa($defaults, $key); if (!is_array($defaultValue)) { // 2016-08-23 // unset добавил сегодня. if (is_null($newValue)) { unset($result[$key]); } else { $result[$key] = $newValue; } } else { if (is_array($newValue)) { $result[$key] = df_extend($defaultValue, $newValue); } else { if (is_null($newValue)) { unset($result[$key]); } else { // Если значение по умолчанию является массивом, // а новое значение не является массивом, // то это наверняка говорит об ошибке программиста. df_error("df_extend: значением по умолчанию ключа «{$key}» является массив {defaultValue}," . "\nоднако программист ошибочно пытается заместить его" . ' значением {newValue} типа «{newType}», что недопустимо:' . "\nзамещаемое значение для массива должно быть либо массивом, либо «null».", ['{defaultValue}' => df_t()->singleLine(df_dump($defaultValue)), '{newType}' => gettype($newValue), '{newValue}' => df_dump($newValue)]); } } } } return $result; }
/** * @throws \Exception * @return void */ private function throwNotMatch() { /** @var string $message */ if (!$this->isSubjectMultiline()) { $message = "Строка «{$this->getSubject()}»" . " не отвечает регулярному выражению «{$this->getPattern()}»."; } else { if (!$this->isSubjectTooLongToReport()) { $message = "Указанный ниже текст не отвечает регулярному выражению «{$this->getPattern()}»:" . "\nНАЧАЛО ТЕКСТА:\n{$this->getSubject()}\nКОНЕЦ ТЕКСТА"; } else { df_report($this->getReportFileName(), $this->getSubject()); $message = "Текст не отвечает регулярному выражению «{$this->getPattern()}»." . "\nТекст смотрите в файле {$this->getReportFilePath()}." . "\nПервые {$this->getSubjectMaxLinesToReport()} строк текста:" . "\nНАЧАЛО:\n{$this->getSubjectReportPart()}\nКОНЕЦ"; } } df_error($message); }
/** * 2016-05-20 * Возвращает название страны для заданной локали (или системной локали по умолчанию) * по 2-буквенному коду по стандарту ISO 3166-1 alpha-2. * https://ru.wikipedia.org/wiki/ISO_3166-1 * @param string $iso2 * @param string|null $locale [optional] * @return string */ function df_country_ctn($iso2, $locale = null) { df_param_iso2($iso2, 0); /** @var string $result */ $result = dfa(df_countries_ctn($locale), $iso2); if (!$result) { df_error('Система не смогла узнать название страны с кодом «%s» для локали «%s».', $iso2, df_locale($locale)); } return $result; }
/** * @param string $message * @param int $stackLevel [optional] * @throws \Exception * @return void */ private static function throwException($message, $stackLevel = 0) { /** * 2015-01-28 * Раньше тут стояло throw $e, что приводило к отображению на экране * диагностического сообщения в неверной кодировке. * @uses df_error() точнее: эта функция в режиме разработчика * отсылает браузеру заголовок HTTP о требуемой кодировке. */ df_error(new \Exception($message, $stackLevel + 1)); }
/** * 2016-08-27 * @used-by \Df\Payment\R\Response::cv() * @param string|null $key [optional] * @return mixed */ private function c($key = null) { $key = $key ?: df_caller_f(); if (!isset($this->{__METHOD__}[$key])) { /** @var mixed|null $result */ $result = dfa($this->configCached(), $key); if (is_null($result)) { df_error("The class %s should define a value for the parameter «{$key}».", get_class($this)); } $this->{__METHOD__}[$key] = $result; } return $this->{__METHOD__}[$key]; }