Beispiel #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;
}
Beispiel #2
0
 /**
  * 2015-11-19
  * @override
  * @see \Df\Framework\Form\Element\Fieldset::onFormInitialized()
  * @used-by \Df\Framework\Plugin\Data\Form\Element\AbstractElement::afterSetForm()
  * @return void
  */
 public function onFormInitialized()
 {
     parent::onFormInitialized();
     $this->addClass('df-array');
     // 2015-12-29
     // Невидимая строка-шаблон.
     df_hide($this->field('template', $this->itemFormElement()));
     /** @var int $itemId */
     $itemId = 0;
     foreach ($this->v() as $key => $data) {
         /** @var string|int $key */
         /** @var string|array(string => mixed) $data */
         /**
         * 2016-07-30
         * Раньше тут стоял код:
         				// 2015-12-30
         				// https://github.com/mage2pro/core/tree/b1f6809b7723d8426636bb892b852f408bdc5650/Framework/view/adminhtml/web/formElement/array/main.js#L131
         				if (\Df\Config\A::FAKE !== $key) {
         					$this->field($itemId++, $this->itemType(), null, $data);
         				}
         * Теперь у нас ключ @see \Df\Config\A::FAKE удаляется в методе
         * @see \Df\Config\Backend\ArrayT::processA()
         * поэтому здесь его уже быть не должно.
         */
         df_assert_ne(\Df\Config\A::FAKE, $key);
         $this->field($itemId++, $this->itemFormElement(), null, $data);
     }
     df_fe_init($this, __CLASS__, df_fa(), [], 'array');
 }
Beispiel #3
0
 /** @return string */
 private function context()
 {
     if (!isset($this->{__METHOD__})) {
         /** @var string $result */
         $result = '';
         if (is_file($this->filePath()) && $this->line()) {
             /** @var string[] $fileContents */
             $fileContents = file($this->filePath());
             if (is_array($fileContents)) {
                 /**
                  * Перенос строки здесь не нужен,
                  * потому что строки с кодом
                  * уже содержат переносы на следующую стоку
                  * http://php.net/manual/function.file.php
                  */
                 /** @var int $fileLength */
                 $fileLength = count($fileContents);
                 /** @var int $radius */
                 $radius = 8;
                 /** @var int $start */
                 $start = max(0, $this->line() - $radius);
                 /** @var int $end */
                 $end = min($fileLength, $start + 2 * $radius);
                 // 2016-07-31
                 // Нам нужна информация именно функции next (caller).
                 if ($this->_next) {
                     /** @var RFA|null $func */
                     $func = $this->_next->functionA();
                     /**
                      * 2016-07-31
                      * Если @uses \ReflectionFunctionAbstract::isInternal() вернёт true,
                      * то @uses \ReflectionFunctionAbstract::getStartLine() и
                      * @uses \ReflectionFunctionAbstract::getEndLine() вернут false.
                      * http://stackoverflow.com/questions/2222142#comment25428181_2222404
                      * isInternal() === TRUE means ->getFileName() and ->getStartLine() will return FALSE
                      */
                     if ($func && !$func->isInternal()) {
                         /** @var int|false $fStart */
                         $fStart = $func->getStartLine();
                         df_assert_ne(false, $fStart);
                         /** @var int|false $fEnd */
                         $fEnd = $func->getEndLine();
                         df_assert_ne(false, $fEnd);
                         // 2016-07-31
                         // http://stackoverflow.com/a/7027198
                         // It's actually - 1, otherwise you wont get the function() block.
                         $start = max($start, $fStart - 1);
                         $end = min($end, $fEnd);
                     }
                 }
                 $result = df_trim(implode(array_slice($fileContents, $start, $end - $start)), $charlist = "\r\n");
             }
         }
         $this->{__METHOD__} = $result;
     }
     return $this->{__METHOD__};
 }
Beispiel #4
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);
}
Beispiel #5
0
 /**
  * 2016-08-31
  * @return string
  */
 private function _p()
 {
     /** @var string $result */
     /**
     * Обратите внимание, что метод ядра Magento CE
     * @see \Magento\Framework\Simplexml\Element::asNiceXml()
     * не сохраняет в документе XML блоки CDATA,
     * а вместо этого заменяет недопустимые для XML символы их допустимыми кодами,
     * например: & => &
     *
     * Также @see \Magento\Framework\Simplexml\Element::asNiceXml()
     * не добавляет к документу заголовок XML: его надо добавить вручную.
     *
     * 2015-02-27
     * Обратите внимание, что для конвертации объекта класса @see SimpleXMLElement в строку
     * надо использовать именно метод @uses SimpleXMLElement::asXML(),
     * а не @see SimpleXMLElement::__toString() или оператор (string)$this.
     *
     * @see SimpleXMLElement::__toString() и (string)$this
     * возвращают непустую строку только для концевых узлов (листьев дерева XML).
     * Пример:
     			<?xml version='1.0' encoding='utf-8'?>
     				<menu>
     					<product>
     						<cms>
     							<class>aaa</class>
     							<weight>1</weight>
     						</cms>
     						<test>
     							<class>bbb</class>
     							<weight>2</weight>
     						</test>
     					</product>
     				</menu>
     * Здесь для $e1 = $xml->{'product'}->{'cms'}->{'class'}
     * мы можем использовать $e1->__toString() и (string)$e1.
     * http://3v4l.org/rAq3F
     * Однако для $e2 = $xml->{'product'}->{'cms'}
     * мы не можем использовать $e2->__toString() и (string)$e2,
     * потому что узел «cms» не является концевым узлом (листом дерева XML).
     * http://3v4l.org/Pkj37
     * Более того, метод @see SimpleXMLElement::__toString()
     * отсутствует в PHP версий 5.2.17 и ниже:
     * http://3v4l.org/Wiia2#v500
     */
     /** @var string $header */
     $header = $this[self::P__SKIP_HEADER] ? '' : df_xml_header($this[self::P__1251] ? 'Windows-1251' : 'UTF-8');
     /** @var X $x */
     $x = df_xml_parse(df_cc_n($header, $this[self::P__DOC_TYPE], sprintf('<%s/>', $this[self::$P__TAG])));
     $x->addAttributes($this[self::P__ATTRIBUTES]);
     $x->importArray($this[self::$P__CONTENTS], $this[self::P__WRAP_IN_CDATA]);
     /** @var bool $pretty */
     $pretty = $this[self::P__PRETTY];
     $result = $this[self::P__SKIP_HEADER] ? $x->asXMLPart() : ($pretty || $this[self::P__1251] ? df_cc_n($header, $pretty ? $x->asNiceXml() : $x->asXMLPart()) : $x->asXML());
     // Убеждаемся, что asXML вернуло строку, а не false.
     df_assert_ne(false, $result);
     /**
      * Символ 0xB (вертикальная табуляция) допустим в UTF-8, но недопустим в XML:
      * http://stackoverflow.com/a/10095901
      */
     $result = str_replace("\v", "&#x0B;", $result);
     if ($this[self::P__1251]) {
         $result = df_1251_to($result);
     }
     if ($this[self::P__REMOVE_LINE_BREAKS]) {
         $result = df_t()->removeLineBreaks($result);
     }
     if ($this[self::P__DECODE_ENTITIES]) {
         $result = html_entity_decode($result, ENT_NOQUOTES, 'UTF-8');
     }
     return $result;
 }