Cyrax_02

Cyrax_02

С нами с 04 августа 2013; Место в рейтинге пользователей: #247
Cyrax_02
14 декабря 2013, 15:58
0
Двойные вызовы MinifyX исключил. Проверил — никаких артефактов
Тем не менее, одна проблемка осталась:

Вызываю MinifyX некэшированным с &forceUpdate = `0`:
[[!MinifyX? &cssSources=`/css/st.css` &minifyCss = `1` &forceUpdate = `0`
               ®isterCss = `placeholder`
               &cacheFolder=`/css/min/` &cssFilename = `css5`]]
[[+MinifyX.css]]
Далее обновляем страницу: после создания минимизированного файла он не обновляется (как и должно быть).
Изменяем файл "/css/st.css". Снова обновляем страницу: первые 6 обновлений минимизированный файл пересоздаётся, с 7-го обновления пересоздаваться перестаёт. Ситуация стабильная. Проверил 5 раз.

Далее пробую другой вариант — вместо плейсхолдера использую средства modx (registerCss = `default`):
[[!MinifyX? &cssSources=`/css/st.css` &minifyCss = `1` &forceUpdate = `0`
               ®isterCss = `default`
               &cacheFolder=`/css/min/` &cssFilename = `css5`]]
Далее обновляем страницу: после создания минимизированного файла он не обновляется (как и должно быть).
Изменяем файл "/css/st.css". Снова обновляем страницу: первые 7 обновлений минимизированный файл пересоздаётся, с 8-го обновления пересоздаваться перестаёт (может, ошибся на 1).

Таким образом, получается, что после изменения исходного файла минимизированный файл при последующих обновлениях страницы пересоздаётся несколько раз, после чего не меняется. Почему он пересоздаётся несколько раз вместо одного?
Cyrax_02
13 декабря 2013, 22:39
0
Да, так и есть. При попытке непосредственной загрузки этой картинки загружается главная страница, а там — тот самый вызов reg_css.
Обошлось таки без экзорцизма. Завтра займусь сервером…
Cyrax_02
13 декабря 2013, 22:24
0
Блин, полтергейст конкретный. Да, все проблемы из-за двойного вызова MinifyX.
Только вот второй (проблемный) вызов этого сниппета — это что-то аномальное. С такой флуктуацией я ещё не сталкивался.

В общем, вызов сниппета MinifyX выполняется в моём сниппете 'reg_css'. Так вот, в шаблоне страницы сниппет 'reg_css' не вызывается. Сниппет 'reg_css' вызывается вот из-за этой строчки в шаблоне:
<img src="images/sun.gif" alt="" class="рcol" />
Здесь путь «images/sun.gif» — неправильный (такого файла нет). Если исправить путь либо убрать всю эту строчку, то сниппет 'reg_css' вызываться не будет и паразитный файл хххххххх.min.css создаваться не будет.

То, что паразитный вызов MinifyX осуществляется из сниппета 'reg_css' — это точно (проверил). Но с какого перепугу вызывается этот сниппет при указании несуществующей картинки — непонятно.

P.S. Всё, завтра с утра к священнику…
Cyrax_02
13 декабря 2013, 19:48
0
Нет, не в плейсхолдере дело.
Дело в том, что Minify где-то запомнил (закэшировал) мой вчерашний путь и содержимое вчерашнего минимизированного файла. И при каждом вызове MinifyX этот вчерашний файл по вчерашнему пути постоянно создаётся. При этом в параметрах MinifyX я указываю совсем другие пути и другие имена файлов. Т.е. нормально создаются минимизированные файлы в соответствии с текущими параметрами сниппета, но дополнительно создаётся (обновляется) ещё и вчерашний файл по вчерашнему пути.

Если указать другую папку или другое имя файла, то ни одного из 3 артефактов не наблюдается (но при этом дополнительно создаётся вчерашний файл по вчерашнему пути).
Если же указать вчерашнюю папку и вчерашнее имя файла, то начинаются пляски с бубном: MinifyX создаёт минимизированный файл (как положено) и тут же восстанавливает хрен знает откуда вчерашний файл, перезаписывая только что созданный (или меняя его номер).

Короче, проблема одна — откуда MinifyX берёт содержимое вчерашнего файла и вчерашний путь?
Кэш modx очищал, кэш браузера полностью очищал, кэширование в браузере полностью отключено.

======================
И ещё одно наблюдение. Если выставить forceUpdate = 0 и изменить что-нибудь в исходном файле, то минимизированный файл пересоздаётся при двух последующих обновлениях страницы, при обновлении в 3-й и последующие разы минимизированный файл остаётся без изменений (как положено).
Этот артефакт наблюдается при любом способе вызова сниппета MinifyX.

Может, это из-за отключенного кэширования в браузере?
Cyrax_02
13 декабря 2013, 18:02
0
Видимо, оттого, что все запускают сниппет так:
[[!MinifyX? &cssSources=`/css/st.css` &minifyCss = `1` &forceUpdate = `0`
            ®isterCss = `placeholder`
            &cacheFolder=`css/min/` &cssFilename = `css1`]]
[[+MinifyX.css]]

А артефакты наблюдаются при таком использовании:
$modx->runSnippet('MinifyX', array('cssSources' => '/css/st.css', 'minifyCss' => 1, 'forceUpdate' => 0,
                                                   'registerCss' => 'placeholder',
                                                   'cacheFolder' => 'css/min/', 'cssFilename' => 'css2'));
return $modx->placeholders['MinifyX.css'];
Cyrax_02
13 декабря 2013, 08:51
0
MinifyX я думаю нужен в первую очередь для того что бы сжать и кэшировать стили в 1 файл.

Первые 2 артефакта как раз и имеют место при обычном сжатии и кэшировании (при 'forceUpdate' = 0):

1. Если сниппет вызывать через runSnippet, то изменение исходного файла не приводит к перегенерации выходного (минимизированного) файла до тех пор, пока не будет очищен кэш modx.

2. Если сниппет вызывать через runSnippet, то сгенерированный в этом случае конечный (минимизированный) файл настолько прочно заседает в кэше modx, что даже при его физическом удалении и очистке кэша modx он всё время пересоздаётся (восстанавливается) заново, даже если мы работает уже с другим минимизированным файлом (имеющим другое базовое имя cssFilename).
Такой артефакт наблюдается только после выполнения 2-го варианта (runSnippet) при последующих выполнениях 1-го варианта — восстанавливается конечный (минимизированный) файл, созданный 2-м вариантом.

Если у вас такая жесткая потребность в постоянном обновлении css то вам этот компонент не нужен.
Речь о 'forceUpdate' = 1?
Потребности в постоянном обновлении у меня нет. В рабочем состоянии у меня 'forceUpdate' = 0. Но ведь этот параметр реализован не просто так. В случае вызова сниппета через runSnippet и 'forceUpdate' = 1 имеет место 3-й артефакт.
Cyrax_02
11 декабря 2013, 23:01
0
++ тоже не работает. Только подстановка системных настроек вроде и не реализована в коде.

P.S. * и ~ (без параметров) — работают. Точно.
Cyrax_02
11 декабря 2013, 22:48
0
в строках 347 и 349 нужно плюсик убрать:
case '+':
    if (isset($this->modx->placeholders['+'.$value])) {
        $src[] = $v[0];
        $dst[] = $this->modx->placeholders['+'.$value];
        $processed = true;
    }
    break;
Cyrax_02
11 декабря 2013, 22:36
0
Стоп. Подстановка системных плейсхолдеров из $modx->placeholders не работает.
Ставим в чанке, например, [[+modx.user.id]], указываем fastMode, делаем print_r — в результате вызов [[+modx.user.id]] остаётся. Если отменить fastMode — значение подставляется нормально (парсер modx).
Cyrax_02
11 декабря 2013, 22:06
0
Проверил. Всё, что реализовано, — работает нормально (++, *, ~, + передаваемые, + из $modx->placeholders).
[[% ]] — не проверял.

В режиме fastMode не работает следующее:
а) вызовы чанков
б) теги, содержащие вложенные теги (насколько я понимаю, вложенные теги подставляются, но затем внешний тег вырезается)
в) ссылки с параметрами, например:
[[~190? &p1=`111` &p2=`222`]]
Cyrax_02
11 декабря 2013, 16:23
0
Так какой из этого всего следует вывод? Что метод pdoTools::getChunk() c fastMode быстрее, но не всё обрабатывает, а без него медленнее?

Да, fastMode обрабатывает $modx->placeholders быстрее, но вырезает все прочие теги.

Получается, что вот эти действия:
а) плейсхолдеры, передаваемые 2-м параметром, обрабатываются как есть (через массив «плейсхолдеры-значения»);
б) стандартные плейсхолдеры $modx->placeholders обрабатываются «точечно», т.к. в большинстве чанков присутствует только малая часть из них (либо отсутствуют вообще);
в) если после обработки в чанке остаются какие-либо теги modx, управление передаётся стандартному парсеру $modx
на базе имеющихся методов pdoTools никак не реализовать (без коррекции кода)?

Если нет, состряпаю что-нибудь сам.
Cyrax_02
11 декабря 2013, 15:41
0
Не обработает. Эти плейсхолдеры останутся и будут процесситься уже потом, при выводе страницы.
Причем, не str_replace, а созданием modTag и вызовом modTag::process(), что будет дольше.

Проверил — не остаются:
$html = $modx->pdoTools->getChunk($chunk, $placeholders, false);
func_dumper($html, true);   // все глобальные плейсхолдеры из $modx->placeholders уже подставлены (в $placeholders их нет)
$html = $modx->pdoTools->fastProcess($html);

И pdoTools::fastProcess() отрабатывает уже впустую.

Вот последний кусок метода getChunk:
if (strpos($content, '[[') !== false) {
    return $fastMode
        ? $this->fastProcess($content, $properties)
        : $chunk['object']->process($properties, $content);   // вызывается парсер modx, который обрабатывает все оставшиеся теги, в т.ч. $modx->placeholders
}
Cyrax_02
11 декабря 2013, 14:42
0
5)
$modx->getChunk($chunkName, $placeholders);
// время: 1.3Х-1.6X сек (чанк обрабатывается полностью)
Cyrax_02
11 декабря 2013, 14:31
0
Учитывая, что время работы парсера в сабжевых тестах не учитывается, получаем:
1)
$modx->pdoTools->getChunk($chunkName, array_merge($placeholders, $modx->placeholders), false);
// время: X сек (чанк обрабатывается полностью)
2)
$modx->pdoTools->getChunk($chunkName, $placeholders, false);
// время: 0.5X сек + время обработки $modx->placeholders парсером modx (чанк обрабатывается полностью)
3)
$modx->pdoTools->getChunk($chunkName, $placeholders, true);
// время: 0.2X сек (вложенные чанки и прочие теги не обрабатываются)
4)
$html = $modx->pdoTools->getChunk($chunkName, $placeholders, false);
$html = $modx->pdoTools->fastProcess($html);
// время: 0,5X (чанк обрабатывается полностью)

Интерес здесь представляют варианты 1,2,4 (чанк обрабатывается полностью). Вариант 4 по скорости лидирует.
Cyrax_02
11 декабря 2013, 12:34
0
пункт
2. Если остаются теги и [modxPaceholders = true], выполняется «точечная» обработка $modx->placeholders
читать так:
2. Если остаются теги '[[+', '[[!+' и [modxPaceholders = true], выполняется «точечная» обработка $modx->placeholders
Cyrax_02
11 декабря 2013, 12:11
0
В третьем вызове идёт точечная проверка ключей в modX::placeholders — это гораздо быстрее.

Хотел было спросить, почему такой же «точечный» метод (на основе collectElementTags) не реализован в отношении плейсхолдеров, передаваемых вторым параметром. Но здесь всё ясно: каждый из плейсхолдеров, передаваемых вторым параметром, заведомо присутствует в чанке. Поэтому, быстрее будет выполнить str_replace для массива всех этих плейсхолдеров. Что касается $modx->placeholders, то в большинстве чанков они либо будут отсутствовать, либо будет присутствовать только малая часть из них. Поэтому, обработку плейсхолдеров $modx->placeholders быстрее выполнить «по факту» (точечно).

Можно попробовать сделать двойную обработку:
// обработка всех переданных плейсхолдеров с условиями
$html = $modx->pdoTools->getChunk($chunkName, $placeholders, false);
// подстановка из modX::placeholders
$html = $modx->pdoTools->fastProcess($html);

А вот здесь идея не ясна. В первом комментарии идёт речь об обработке плейсхолдеров с условиями. О каких условиях речь? Здесь первый вызов getChunk так и так обработает все плейсхолдеры, включая $modx->placeholders (путём вызова стандартного парсера $modx). И вызов fastProcess уже будет не нужен (отработает «вхолостую»).

===================================================================================
Сабжевая задача выглядит следующим образом:
а) плейсхолдеры, передаваемые 2-параметром, должны быть обработаны как есть (через массив «плейсхолдеры-значения»), т.к. все они присутствуют в чанке
б) стандартные плейсхолдеры $modx->placeholders обрабатываются «точечно», т.к. в чанке присутствует только малая часть из них (либо отсутствуют вообще)
в) если после обработки в чанке остаются какие-либо теги modx, управление передаётся стандартному парсеру $modx

Сейчас (без переделки кода) возможно реализовать только такие комбинации:
— либо (а + в)
— либо (а + б) // fastMode

====================================================================================
Один из вариантов — методу getChunk вместо параметра fastMode передавать 2 параметра:
— modxPlaceholders — обрабатывать ли $modx->placeholders (обработка выполняется точечно)
— modxParser — передавать ли обработку стандартному парсеру modx, если остаются теги

А алгоритм будет таким:
1. Выполняется обработка quick placeholders, standard placeholders, lexicon placeholders (как сейчас)
2. Если остаются теги и [modxPaceholders = true], выполняется «точечная» обработка $modx->placeholders
3. Если остаются теги и [modxParser = true], вызывается стандартный парсер modx

Такой вариант является более гибким и более быстрым (для случаев, когда чанк содержит стандартные плейсхолдеры modx), а режим fastMode будет равносилен набору параметров [modxPlaceholders = true], [modxParser = false].

==============================================================================
Другой вариант: оставляем параметр fastMode, но при этом «точечная» обработка $modx->placeholders выполняется всегда (если остаются теги modx). При [fastMode = true] обработка стандартному парсеру не передаётся никогда, при [fastMode = false] и при наличии тегов modx обработка передаётся стандартному парсеру.
В данном варианте параметр fastMode целесообразно будет переименовать, например, в useModxParser.

Но я за параметры modxPlaceholders и modxParser — это более гибкий вариант.
Cyrax_02
10 декабря 2013, 20:50
0
Режим fastMode не использую в сабжевых целях, т.к. не обрабатываются вложенные чанки и прочие теги modx.

Провёл тест суммарного времени выполнения методов pdoTools::getChunk() на странице с большим числом вызовов чанков. Результаты получились такие:
1) $modx->pdoTools->getChunk($chunkName, array_merge($placeholders, $modx->placeholders), false); // время: X сек
2) $modx->pdoTools->getChunk($chunkName, $placeholders, false); // время: 0.5X сек
3) $modx->pdoTools->getChunk($chunkName, $placeholders, true); // время: 0.2X сек

То, что 2-й вариант отработал вдвое быстрее 1-го варианта, можно объяснить тем, что у меня в большинстве чанков размещены только те плейхолдеры, которые передаются вторым параметром в pdoTools::getChunk():
— в первом варианте для каждого вызова pdoTools::getChunk() идёт вызов метода str_replace для большого числа плейсхолдеров (из которых в чанке содержатся только некоторые), но при этом до стандартного парсера modx дело не доходит;
— во 2-м варианте метод str_replace вызывается только для малого числа плейсхолдеров, которые заведомо присутствуют в чанках, при этом стандартный парсер modx вызывается только для единичных чанков, в которых присутствуют плейсхолдеры, не переданные вторым параметром в pdoTools::getChunk().
Таким образом, вызовы st_replace для большого числа плейсхолдеров по времени вдвое перевешивают редкие вызовы стандартного парсера modx.

Но не пойму я, почему 3-й вариант (fastMode c «ручной» обработкой всех плейсхолдеров из $modx->placeholders) выполняется в 5 раз быстрее, чем 1-й вариант. Ведь в обоих этих случаях выполняется «ручная» обработка всех плейсхолдеров (переданных 2-м параметром + $modx->placeholders) и в обоих случаях до стандартного парсера modx дело не доходит.
Cyrax_02
28 октября 2013, 16:01
0
Самую последнюю строчку читать так:
Этот оператор ничего не будет возвращать для tv, не содержащих значений, т.к. в итоговом запросе вместо (tv IS NULL) получим ('' IS NULL)
Cyrax_02
26 октября 2013, 16:11
0
И ещё одна оптимизация. В предложении WHERE (как для стандартных полей `properties`, `uri`, `introtext`, `alias`, так и для пользовательских TV-полей) добавлять проверку на NULL (IFNULL/ISNULL) достаточно только в 2 случаях:
1. Если поле сравнивается с некоторым значением операцией «не равно»:
..., "tv1:!=":"5", ...
Сейчас такому условию НЕ будут удовлетворять ресурсы, для которых значение поля tv1 не указано. А должны удовлетворять.
2. Если поле сравнивается с пустым значением:
..., "tv1:=":"", ...
..., "tv1":"", ...
Сейчас такому условию НЕ будет удовлетворять ни один ресурс. А должны удовлетворять все ресурсы, для которых не задано значение поля tv1.

Во всех остальных случаях подстановка IFNULL/ISNULL не нужна. Все прочие условия будут работать корректно и без IFNULL/ISNULL.

— Почему в предложении WHERE важно не добавлять IFNULL/ISNULL в безусловном порядке для всех полей, а делать это только для двух вышеуказанных случаев:
а) ускорение выполнения запроса (т.к. каждый IFNULL/ISNULL увеличивает время выполнения запроса)
б) если IFNULL/ISNULL ставить всегда, то пользователь не будет иметь возможности корректно использовать оператор is null:
..., "tv1:IS":"null", ...
Этот оператор ничего не будет возвращать, т.к. в итоговом запросе вместо (tv1 IS NULL) получим (tv1 IS '').
Cyrax_02
26 октября 2013, 11:42
0
Да, и касательно IFNULL. В целях сохранения переносимости я бы сделал так:
switch($modx->getOption('dbtype')) {
    case 'mysql': $dbObjectNameBorder = '`'; $nullCheckExpr = 'IFNULL'; break;
    case 'sqlsrv': $dbObjectNameBorder = ''; $nullCheckExpr = 'ISNULL'; break;
}
и далее использовал $dbObjectNameBorder и $nullCheckExpr. Такой вариант будет работать и с mySQL, и с MS SQL.