Пример #1
0
/**
 * @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;
}
Пример #2
0
/**
 * 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;
}
Пример #3
0
 /**
  * @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;
 }
Пример #4
0
 /**
  * @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;
 }
Пример #5
0
 /**
  * @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;
 }
Пример #6
0
 /**
  * @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()}».");
     }
 }
Пример #7
0
 /**
  * @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;
 }
Пример #8
0
/**
 * @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;
}
Пример #9
0
 /**
  * @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];
 }
Пример #10
0
/**
 * 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;
}
Пример #11
0
 /**
  * По поводу удаления @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');
 }
Пример #12
0
 /**
  * @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;
 }
Пример #13
0
 /**
  * 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];
 }
Пример #14
0
/**
 * 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));
    }
}
Пример #15
0
/**
 * 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.");
}
Пример #16
0
 /**
  * @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;
 }
Пример #17
0
/**
 * 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);
}
Пример #18
0
 /**
  * @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;
 }
Пример #19
0
/**
 * Иногда я для разработки использую заплатку ядра для 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);
}
Пример #20
0
 /**
  * @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);
 }
Пример #21
0
/**
 * 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)));
}
Пример #22
0
/**
 * 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());
}
Пример #23
0
/**
 * Возвращает неиспользуемое имя файла в заданной папке $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);
}
Пример #24
0
 /**
  * @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__};
 }
Пример #25
0
 /**
  * @used-by df_throw_last_error()
  * @return void
  * @throws Exception
  */
 public static function throwLast()
 {
     df_assert(error_get_last());
     df_error(self::i()->main());
 }
Пример #26
0
/**
 * 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;
}
Пример #27
0
 /**
  * @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);
 }
Пример #28
0
/**        
 * 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;
}
Пример #29
0
 /**
  * @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));
 }
Пример #30
0
 /**
  * 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];
 }