raise editorial quality gate and revise 2018 spring
Build and deploy / deploy (push) Successful in 15s
@@ -0,0 +1,66 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 700" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Построение символьного кода элемента Bitrix</title>
|
||||
<desc id="desc">Схема показывает путь от названия элемента через транслитерацию и проверку занятости кода к сохранению элемента или добавлению суффикса.</desc>
|
||||
<defs>
|
||||
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
||||
<path d="M0,0 L12,6 L0,12 z" fill="#264653"/>
|
||||
</marker>
|
||||
<style>
|
||||
.bg { fill: #f7f4ec; }
|
||||
.node { fill: #ffffff; stroke: #264653; stroke-width: 3; }
|
||||
.accent { fill: #e9c46a; stroke: #264653; stroke-width: 3; }
|
||||
.success { fill: #a8dadc; stroke: #264653; stroke-width: 3; }
|
||||
.warn { fill: #f4a261; stroke: #264653; stroke-width: 3; }
|
||||
.line { fill: none; stroke: #264653; stroke-width: 3; marker-end: url(#arrow); }
|
||||
.dash { fill: none; stroke: #264653; stroke-width: 3; stroke-dasharray: 10 8; marker-end: url(#arrow); }
|
||||
.label { font: 700 25px Arial, sans-serif; fill: #1d3557; }
|
||||
.text { font: 20px Arial, sans-serif; fill: #264653; }
|
||||
.code { font: 700 19px "Courier New", monospace; fill: #1d3557; }
|
||||
.small { font: 17px Arial, sans-serif; fill: #457b9d; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="bg" width="1200" height="700" rx="28"/>
|
||||
<text x="60" y="68" class="label">Символьный код — это цепочка проверок, а не только транслит</text>
|
||||
|
||||
<rect x="58" y="165" width="190" height="155" rx="18" class="node"/>
|
||||
<text x="83" y="214" class="label">1. Имя</text>
|
||||
<text x="83" y="252" class="text">«Кофе Classic</text>
|
||||
<text x="83" y="280" class="text">250 г»</text>
|
||||
|
||||
<path d="M248 243 H337" class="line"/>
|
||||
|
||||
<rect x="347" y="150" width="220" height="185" rx="18" class="accent"/>
|
||||
<text x="373" y="199" class="label">2. CUtil::</text>
|
||||
<text x="373" y="230" class="label">translit</text>
|
||||
<text x="373" y="270" class="small">нижний регистр,</text>
|
||||
<text x="373" y="296" class="small">дефис вместо пробела</text>
|
||||
|
||||
<path d="M567 243 H658" class="line"/>
|
||||
|
||||
<rect x="668" y="165" width="210" height="155" rx="18" class="node"/>
|
||||
<text x="695" y="214" class="label">3. Кандидат</text>
|
||||
<text x="695" y="257" style="font: 700 16px "Courier New", monospace; fill: #1d3557;">kofe-classic-250-g</text>
|
||||
|
||||
<path d="M878 243 H962" class="line"/>
|
||||
|
||||
<rect x="972" y="150" width="180" height="185" rx="18" class="node"/>
|
||||
<text x="997" y="198" class="label">4. GetList</text>
|
||||
<text x="997" y="233" class="small">IBLOCK_ID</text>
|
||||
<text x="997" y="260" class="small">+ CODE</text>
|
||||
<text x="997" y="291" class="small">есть запись?</text>
|
||||
|
||||
<path d="M1062 335 V438 H932" class="line"/>
|
||||
<rect x="722" y="408" width="200" height="125" rx="18" class="success"/>
|
||||
<text x="748" y="457" class="label">Свободен</text>
|
||||
<text x="748" y="493" class="text">передаём в Add</text>
|
||||
|
||||
<path d="M972 243 H930 V580 H710" class="dash"/>
|
||||
<rect x="460" y="534" width="240" height="120" rx="18" class="warn"/>
|
||||
<text x="487" y="580" class="label">Занят</text>
|
||||
<text x="487" y="616" class="text">добавляем -2, -3…</text>
|
||||
<path d="M460 594 H345 V380 H770 V320" class="dash"/>
|
||||
|
||||
<text x="938" y="412" class="small">нет</text>
|
||||
<text x="884" y="566" class="small">да</text>
|
||||
<text x="58" y="676" class="small">Проверка по списку полезна для последовательного ввода. При параллельном импорте нужен отдельный проектный способ исключить гонку.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 700" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Диагностика конфликта символьного кода Bitrix</title>
|
||||
<desc id="desc">Дерево проверки для ситуации, когда адрес карточки открывает не тот элемент: путь, переменные, выборка и набор совпадений.</desc>
|
||||
<defs>
|
||||
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
||||
<path d="M0,0 L12,6 L0,12 z" fill="#3d405b"/>
|
||||
</marker>
|
||||
<style>
|
||||
.bg { fill: #fff8ed; }
|
||||
.node { fill: #ffffff; stroke: #3d405b; stroke-width: 3; }
|
||||
.focus { fill: #d8e7ff; stroke: #3d405b; stroke-width: 3; }
|
||||
.ok { fill: #cce8d4; stroke: #3d405b; stroke-width: 3; }
|
||||
.warn { fill: #f8d49c; stroke: #3d405b; stroke-width: 3; }
|
||||
.bad { fill: #f6b3a8; stroke: #3d405b; stroke-width: 3; }
|
||||
.line { fill: none; stroke: #3d405b; stroke-width: 3; marker-end: url(#arrow); }
|
||||
.label { font: 700 25px Arial, sans-serif; fill: #3d405b; }
|
||||
.text { font: 20px Arial, sans-serif; fill: #3d405b; }
|
||||
.code { font: 700 18px "Courier New", monospace; fill: #3d405b; }
|
||||
.small { font: 17px Arial, sans-serif; fill: #5c677d; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="bg" width="1200" height="700" rx="28"/>
|
||||
<text x="56" y="67" class="label">Карточка открывает не тот товар: сначала считаем совпадения</text>
|
||||
|
||||
<rect x="462" y="108" width="278" height="112" rx="18" class="focus"/>
|
||||
<text x="491" y="155" class="label">Ожидаемый URL</text>
|
||||
<text x="491" y="190" class="code">.../classic-250-g/</text>
|
||||
|
||||
<path d="M601 220 V296" class="line"/>
|
||||
<rect x="403" y="306" width="396" height="116" rx="18" class="node"/>
|
||||
<text x="430" y="352" class="label">Что компонент получил?</text>
|
||||
<text x="430" y="389" class="code">ELEMENT_CODE = classic-250-g</text>
|
||||
|
||||
<path d="M601 422 V495" class="line"/>
|
||||
<rect x="360" y="425" width="480" height="92" rx="18" class="focus"/>
|
||||
<text x="390" y="466" class="label">GetList по этому CODE</text>
|
||||
<text x="390" y="495" class="small">в том же IBLOCK_ID и с теми же фильтрами</text>
|
||||
|
||||
<path d="M360 471 H288 V594 H294" class="line"/>
|
||||
<rect x="56" y="544" width="228" height="98" rx="18" class="warn"/>
|
||||
<text x="80" y="585" class="label">0 записей</text>
|
||||
<text x="80" y="613" class="small">шаблон, фильтр, активность</text>
|
||||
|
||||
<path d="M840 471 H912 V594 H906" class="line"/>
|
||||
<rect x="916" y="544" width="228" height="98" rx="18" class="bad"/>
|
||||
<text x="940" y="585" class="label">2+ записи</text>
|
||||
<text x="940" y="613" class="small">конфликт CODE или фильтр</text>
|
||||
|
||||
<path d="M600 517 V558" class="line"/>
|
||||
<rect x="438" y="558" width="324" height="84" rx="18" class="ok"/>
|
||||
<text x="469" y="610" style="font: 700 23px Arial, sans-serif; fill: #3d405b;">1 запись: сверяем ID</text>
|
||||
|
||||
<text x="56" y="676" class="small">Кеш проверяем после данных и маршрута. Иначе он становится удобным, но недоказанным объяснением.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1,64 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 700" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Путь URL до элемента инфоблока в Bitrix</title>
|
||||
<desc id="desc">Диаграмма показывает как адрес страницы разбирается шаблоном SEF, превращается в переменные и используется для поиска элемента инфоблока.</desc>
|
||||
<defs>
|
||||
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
||||
<path d="M0,0 L12,6 L0,12 z" fill="#1d3557"/>
|
||||
</marker>
|
||||
<style>
|
||||
.bg { fill: #f5f7fb; }
|
||||
.node { fill: #ffffff; stroke: #1d3557; stroke-width: 3; }
|
||||
.route { fill: #cde8f0; stroke: #1d3557; stroke-width: 3; }
|
||||
.query { fill: #f7d488; stroke: #1d3557; stroke-width: 3; }
|
||||
.result { fill: #b9e3c6; stroke: #1d3557; stroke-width: 3; }
|
||||
.fail { fill: #f6b3a8; stroke: #1d3557; stroke-width: 3; }
|
||||
.line { fill: none; stroke: #1d3557; stroke-width: 3; marker-end: url(#arrow); }
|
||||
.label { font: 700 25px Arial, sans-serif; fill: #1d3557; }
|
||||
.text { font: 20px Arial, sans-serif; fill: #264653; }
|
||||
.code { font: 700 18px "Courier New", monospace; fill: #1d3557; }
|
||||
.small { font: 17px Arial, sans-serif; fill: #457b9d; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="bg" width="1200" height="700" rx="28"/>
|
||||
<text x="56" y="67" class="label">Адрес не ищет запись сам: компонент сначала восстанавливает переменные</text>
|
||||
|
||||
<rect x="56" y="160" width="238" height="154" rx="18" class="node"/>
|
||||
<text x="82" y="209" class="label">Запрос браузера</text>
|
||||
<text x="82" y="254" class="code">/catalog/kofe/</text>
|
||||
<text x="82" y="281" class="code">classic-250-g/</text>
|
||||
|
||||
<path d="M294 237 H380" class="line"/>
|
||||
|
||||
<rect x="390" y="135" width="256" height="205" rx="18" class="route"/>
|
||||
<text x="416" y="185" class="label">SEF-шаблон</text>
|
||||
<text x="416" y="227" class="code">#SECTION_CODE#/</text>
|
||||
<text x="416" y="255" class="code">#ELEMENT_CODE#/</text>
|
||||
<text x="416" y="301" class="small">ParseComponentPath</text>
|
||||
|
||||
<path d="M646 237 H731" class="line"/>
|
||||
|
||||
<rect x="741" y="135" width="240" height="205" rx="18" class="query"/>
|
||||
<text x="767" y="184" class="label">Переменные</text>
|
||||
<text x="767" y="225" class="code">SECTION_CODE</text>
|
||||
<text x="767" y="254" class="code">ELEMENT_CODE</text>
|
||||
<text x="767" y="301" class="small">не запись в БД</text>
|
||||
|
||||
<path d="M981 237 H1066" class="line"/>
|
||||
|
||||
<rect x="1076" y="160" width="84" height="154" rx="18" class="node"/>
|
||||
<text x="1093" y="209" class="label">Query</text>
|
||||
<text x="1092" y="253" class="small">CODE</text>
|
||||
<text x="1092" y="279" class="small">+ filter</text>
|
||||
|
||||
<path d="M1118 314 V431 H941" class="line"/>
|
||||
<rect x="692" y="402" width="239" height="133" rx="18" class="result"/>
|
||||
<text x="720" y="452" class="label">Элемент найден</text>
|
||||
<text x="720" y="490" class="text">детальная страница</text>
|
||||
|
||||
<path d="M1076 239 H1018 V592 H839" class="line"/>
|
||||
<rect x="600" y="564" width="229" height="95" rx="18" class="fail"/>
|
||||
<text x="625" y="606" class="label">Нет совпадения</text>
|
||||
<text x="625" y="635" class="text">404 или другой путь</text>
|
||||
|
||||
<text x="56" y="676" class="small">Если шаблон, имя переменной и фильтр расходятся, менять CODE бесполезно: сначала надо увидеть, какое значение восстановлено из URL.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 620" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Три уровня результата cURL-интеграции</title>
|
||||
<desc id="desc">После curl_exec сначала проверяется false и ошибка транспорта, затем код HTTP, а затем контракт тела ответа.</desc>
|
||||
<defs>
|
||||
<linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#1f2437"/>
|
||||
<stop offset="1" stop-color="#3b315f"/>
|
||||
</linearGradient>
|
||||
<marker id="flow-arrow" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
||||
<path d="M0 0 L10 5 L0 10z" fill="#f8e9ac"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="1200" height="620" rx="34" fill="url(#background)"/>
|
||||
<text x="74" y="88" fill="#fff9e4" font-family="Arial, sans-serif" font-size="32" font-weight="700">cURL отвечает за передачу, а не за успех операции</text>
|
||||
<text x="74" y="125" fill="#d1c5e6" font-family="Arial, sans-serif" font-size="19">Проверки идут слева направо: транспорт → HTTP → полезная нагрузка</text>
|
||||
<g font-family="Arial, sans-serif">
|
||||
<rect x="75" y="228" width="194" height="116" rx="18" fill="#f8e9ac"/>
|
||||
<text x="109" y="275" fill="#352f52" font-size="22" font-weight="700">curl_exec()</text>
|
||||
<text x="118" y="310" fill="#352f52" font-size="17">получить тело</text>
|
||||
<path d="M269 286 H385" fill="none" stroke="#f8e9ac" stroke-width="5" marker-end="url(#flow-arrow)"/>
|
||||
<rect x="405" y="228" width="200" height="116" rx="18" fill="#e4d7ff"/>
|
||||
<text x="438" y="274" fill="#352f52" font-size="22" font-weight="700">body === false?</text>
|
||||
<text x="450" y="310" fill="#352f52" font-size="17">cURL + сеть</text>
|
||||
<path d="M505 344 V439 H325" fill="none" stroke="#f8e9ac" stroke-width="5" marker-end="url(#flow-arrow)"/>
|
||||
<text x="422" y="405" fill="#f8e9ac" font-size="16">да</text>
|
||||
<rect x="112" y="438" width="214" height="104" rx="18" fill="#f2b8ae"/>
|
||||
<text x="145" y="481" fill="#5d2427" font-size="21" font-weight="700">transport error</text>
|
||||
<text x="151" y="514" fill="#5d2427" font-size="16">errno, error, time</text>
|
||||
<path d="M605 286 H695" fill="none" stroke="#f8e9ac" stroke-width="5" marker-end="url(#flow-arrow)"/>
|
||||
<text x="636" y="265" fill="#f8e9ac" font-size="16">нет</text>
|
||||
<rect x="715" y="228" width="182" height="116" rx="18" fill="#b8e8e0"/>
|
||||
<text x="745" y="274" fill="#21423f" font-size="22" font-weight="700">HTTP 2xx?</text>
|
||||
<text x="737" y="310" fill="#21423f" font-size="17">curl_getinfo</text>
|
||||
<path d="M806 344 V439 H965" fill="none" stroke="#f8e9ac" stroke-width="5" marker-end="url(#flow-arrow)"/>
|
||||
<text x="842" y="405" fill="#f8e9ac" font-size="16">нет</text>
|
||||
<rect x="966" y="438" width="170" height="104" rx="18" fill="#f2b8ae"/>
|
||||
<text x="1003" y="481" fill="#5d2427" font-size="21" font-weight="700">HTTP error</text>
|
||||
<text x="992" y="514" fill="#5d2427" font-size="16">status + route</text>
|
||||
<path d="M897 286 H995" fill="none" stroke="#f8e9ac" stroke-width="5" marker-end="url(#flow-arrow)"/>
|
||||
<text x="927" y="265" fill="#f8e9ac" font-size="16">да</text>
|
||||
<rect x="1015" y="228" width="121" height="116" rx="18" fill="#d4e8ff"/>
|
||||
<text x="1045" y="274" fill="#294465" font-size="21" font-weight="700">Тело</text>
|
||||
<text x="1032" y="310" fill="#294465" font-size="16">контракт</text>
|
||||
</g>
|
||||
<text x="75" y="585" fill="#d1c5e6" font-family="Arial, sans-serif" font-size="18">Статус 404 или 500 может прийти как строка: это не false для curl_exec().</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,73 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 900" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Контракт Ajax-формы в legacy jQuery</title>
|
||||
<desc id="desc">Диаграмма показывает состояния формы: готова, запрос отправлен, успешный или ошибочный ответ, затем обязательное освобождение интерфейса в обработчике always.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #fffdfa; }
|
||||
.title { fill: #1e252d; font: 700 39px Arial, sans-serif; }
|
||||
.sub { fill: #64707d; font: 25px Arial, sans-serif; }
|
||||
.state { stroke-width: 3; }
|
||||
.idle { fill: #eef3fa; stroke: #355c9a; }
|
||||
.request { fill: #fff4e8; stroke: #b4432a; }
|
||||
.success { fill: #e5f2ed; stroke: #216869; }
|
||||
.failure { fill: #f9eceb; stroke: #a43228; }
|
||||
.final { fill: #f3f6f9; stroke: #64707d; }
|
||||
.state-title { fill: #1e252d; font: 700 30px Arial, sans-serif; text-anchor: middle; }
|
||||
.state-copy { fill: #44515d; font: 23px Arial, sans-serif; text-anchor: middle; }
|
||||
.code { fill: #18385d; font: 700 18px "Courier New", monospace; text-anchor: middle; }
|
||||
.arrow { fill: none; stroke: #64707d; stroke-width: 6; marker-end: url(#arrow); }
|
||||
.safe { fill: #65492e; font: 700 26px Arial, sans-serif; text-anchor: middle; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="14" markerHeight="14" refX="11" refY="7" orient="auto">
|
||||
<path d="M0,0 L14,7 L0,14 z" fill="#64707d"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" width="1200" height="900"/>
|
||||
<text class="title" x="65" y="72">Ajax-форма: интерфейс возвращается</text>
|
||||
<text class="title" x="65" y="115">в готовое состояние</text>
|
||||
<text class="sub" x="65" y="158">Клиентский флаг убирает повторный submit в текущем DOM, а always освобождает кнопку.</text>
|
||||
|
||||
<rect class="state idle" x="75" y="235" width="235" height="150" rx="25"/>
|
||||
<text class="state-title" x="192" y="286">1. Готова</text>
|
||||
<text class="state-copy" x="192" y="327">Кнопка доступна,</text>
|
||||
<text class="state-copy" x="192" y="358">запроса нет.</text>
|
||||
|
||||
<path class="arrow" d="M310 310 H410"/>
|
||||
|
||||
<rect class="state request" x="410" y="215" width="380" height="190" rx="25"/>
|
||||
<text class="state-title" x="600" y="267">2. submit</text>
|
||||
<text class="code" x="600" y="302">data('request')</text>
|
||||
<text class="code" x="600" y="329">+ prop('disabled')</text>
|
||||
<text class="state-copy" x="600" y="367">Один jqXHR хранится на форме.</text>
|
||||
<text class="state-copy" x="600" y="393">Повторный submit выходит сразу.</text>
|
||||
|
||||
<path class="arrow" d="M790 310 H885"/>
|
||||
|
||||
<rect class="state final" x="885" y="235" width="240" height="150" rx="25"/>
|
||||
<text class="state-title" x="1005" y="286">3. jqXHR</text>
|
||||
<text class="state-copy" x="1005" y="327">Серверный ответ</text>
|
||||
<text class="state-copy" x="1005" y="358">или ошибка сети.</text>
|
||||
|
||||
<path class="arrow" d="M1005 385 V500 H795"/>
|
||||
<path class="arrow" d="M1005 385 V500 H405"/>
|
||||
|
||||
<rect class="state success" x="625" y="535" width="340" height="155" rx="25"/>
|
||||
<text class="state-title" x="795" y="584">4a. done</text>
|
||||
<text class="state-copy" x="795" y="625">Показать подтверждённый</text>
|
||||
<text class="state-copy" x="795" y="656">результат сервера.</text>
|
||||
|
||||
<rect class="state failure" x="235" y="535" width="340" height="155" rx="25"/>
|
||||
<text class="state-title" x="405" y="584">4b. fail</text>
|
||||
<text class="state-copy" x="405" y="625">Показать ошибку и не</text>
|
||||
<text class="state-copy" x="405" y="656">считать отправку успехом.</text>
|
||||
|
||||
<path class="arrow" d="M405 690 V758 H600"/>
|
||||
<path class="arrow" d="M795 690 V758 H600"/>
|
||||
|
||||
<rect class="state final" x="415" y="780" width="370" height="75" rx="20"/>
|
||||
<text class="code" x="600" y="816">always: removeData</text>
|
||||
<text class="code" x="600" y="841">+ disabled(false)</text>
|
||||
|
||||
<text class="safe" x="600" y="885">Ограничение: клиент не заменяет серверную защиту от повторной операции.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,69 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 890" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Прямая и делегированная привязка событий после замены HTML</title>
|
||||
<desc id="desc">Сравнение двух подходов: прямой обработчик находится на кнопке и исчезает при замене содержимого контейнера, делегированный обработчик остаётся на постоянном контейнере и получает события от новой кнопки.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #fffdfa; }
|
||||
.title { fill: #1e252d; font: 700 40px Arial, sans-serif; }
|
||||
.sub { fill: #64707d; font: 25px Arial, sans-serif; }
|
||||
.panel { fill: #ffffff; stroke-width: 3; }
|
||||
.bad { stroke: #b4432a; }
|
||||
.good { stroke: #216869; }
|
||||
.panel-title { fill: #1e252d; font: 700 31px Arial, sans-serif; text-anchor: middle; }
|
||||
.copy { fill: #44515d; font: 23px Arial, sans-serif; text-anchor: middle; }
|
||||
.code { fill: #18385d; font: 700 17px "Courier New", monospace; text-anchor: middle; }
|
||||
.root { fill: #eef3fa; stroke: #355c9a; stroke-width: 3; }
|
||||
.old { fill: #fff4e8; stroke: #b4432a; stroke-width: 3; }
|
||||
.new { fill: #e5f2ed; stroke: #216869; stroke-width: 3; }
|
||||
.handler { fill: #f3f6f9; stroke: #64707d; stroke-width: 2; }
|
||||
.cross { stroke: #b4432a; stroke-width: 7; stroke-linecap: round; }
|
||||
.arrow { fill: none; stroke: #64707d; stroke-width: 5; marker-end: url(#arrow); }
|
||||
.bubble { fill: none; stroke: #216869; stroke-width: 5; stroke-dasharray: 12 10; marker-end: url(#arrow-green); }
|
||||
.note { fill: #65492e; font: 700 26px Arial, sans-serif; text-anchor: middle; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="14" markerHeight="14" refX="11" refY="7" orient="auto">
|
||||
<path d="M0,0 L14,7 L0,14 z" fill="#64707d"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="14" markerHeight="14" refX="11" refY="7" orient="auto">
|
||||
<path d="M0,0 L14,7 L0,14 z" fill="#216869"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" width="1200" height="890"/>
|
||||
<text class="title" x="65" y="82">После container.html(): что остаётся?</text>
|
||||
<text class="sub" x="65" y="126">Событие переживает замену DOM только тогда, когда привязано к постоянному предку.</text>
|
||||
|
||||
<rect class="panel bad" x="60" y="190" width="510" height="570" rx="28"/>
|
||||
<text class="panel-title" x="315" y="245">Прямая привязка к кнопке</text>
|
||||
<text class="code" x="315" y="282">$('.js-remove')</text>
|
||||
<text class="code" x="315" y="310">.on('click', handler)</text>
|
||||
|
||||
<rect class="root" x="125" y="335" width="380" height="170" rx="20"/>
|
||||
<text class="copy" x="315" y="378">#cart получает новый HTML</text>
|
||||
<rect class="old" x="210" y="414" width="210" height="60" rx="12"/>
|
||||
<text class="copy" x="315" y="452">старая кнопка</text>
|
||||
<rect class="handler" x="160" y="530" width="310" height="70" rx="15"/>
|
||||
<text class="copy" x="315" y="563">обработчик жил</text>
|
||||
<text class="copy" x="315" y="589">на дочернем узле</text>
|
||||
<path class="arrow" d="M315 505 V526"/>
|
||||
<path class="cross" d="M256 636 L374 704 M374 636 L256 704"/>
|
||||
<text class="copy" x="315" y="735">Новая кнопка создана без него.</text>
|
||||
|
||||
<rect class="panel good" x="630" y="190" width="510" height="570" rx="28"/>
|
||||
<text class="panel-title" x="885" y="245">Делегирование от #cart</text>
|
||||
<text class="code" x="885" y="282">$('#cart').on('click.cart',</text>
|
||||
<text class="code" x="885" y="310">'.js-remove', handler)</text>
|
||||
|
||||
<rect class="root" x="695" y="335" width="380" height="235" rx="20"/>
|
||||
<text class="copy" x="885" y="378">#cart остаётся в DOM</text>
|
||||
<rect class="handler" x="748" y="402" width="274" height="58" rx="14"/>
|
||||
<text class="copy" x="885" y="439">обработчик на контейнере</text>
|
||||
<rect class="new" x="780" y="487" width="210" height="60" rx="12"/>
|
||||
<text class="copy" x="885" y="525">новая кнопка</text>
|
||||
<path class="bubble" d="M885 487 V463"/>
|
||||
<text class="copy" x="885" y="620">Клик всплывает до #cart.</text>
|
||||
<text class="copy" x="885" y="655">Перепривязывать кнопку не нужно.</text>
|
||||
|
||||
<rect x="160" y="800" width="880" height="54" rx="16" fill="#f3f6f9" stroke="#64707d" stroke-width="2"/>
|
||||
<text class="note" x="600" y="836">Корень — ближайший постоянный контейнер.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1,58 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 790" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Повторная инициализация jQuery-виджета без дублирования обработчиков</title>
|
||||
<desc id="desc">Схема показывает, как повторный вызов функции инициализации снимает только свои обработчики через пространство имён и затем назначает один новый обработчик.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #fffdfa; }
|
||||
.title { fill: #1e252d; font: 700 42px Arial, sans-serif; }
|
||||
.sub { fill: #64707d; font: 26px Arial, sans-serif; }
|
||||
.card { stroke-width: 3; }
|
||||
.warm { fill: #fff4e8; stroke: #b4432a; }
|
||||
.blue { fill: #eef3fa; stroke: #355c9a; }
|
||||
.green { fill: #e5f2ed; stroke: #216869; }
|
||||
.card-title { fill: #1e252d; font: 700 30px Arial, sans-serif; text-anchor: middle; }
|
||||
.card-copy { fill: #44515d; font: 24px Arial, sans-serif; text-anchor: middle; }
|
||||
.code { fill: #18385d; font: 700 21px "Courier New", monospace; text-anchor: middle; }
|
||||
.note { fill: #65492e; font: 700 28px Arial, sans-serif; text-anchor: middle; }
|
||||
.small { fill: #44515d; font: 23px Arial, sans-serif; text-anchor: middle; }
|
||||
.arrow { fill: none; stroke: #64707d; stroke-width: 6; marker-end: url(#arrow); }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="14" markerHeight="14" refX="11" refY="7" orient="auto">
|
||||
<path d="M0,0 L14,7 L0,14 z" fill="#64707d"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" width="1200" height="790"/>
|
||||
<text class="title" x="70" y="78">Повторный mount: один обработчик</text>
|
||||
<text class="title" x="70" y="126">при любом числе вызовов</text>
|
||||
<text class="sub" x="70" y="170">Пространство имён отделяет события виджета от остального legacy-кода.</text>
|
||||
|
||||
<rect class="card warm" x="70" y="245" width="300" height="235" rx="28"/>
|
||||
<text class="card-title" x="220" y="300">1. mount(root)</text>
|
||||
<text class="card-copy" x="220" y="345">Виджет вызывают</text>
|
||||
<text class="card-copy" x="220" y="380">после Ajax, таба</text>
|
||||
<text class="card-copy" x="220" y="415">или повторного рендера.</text>
|
||||
|
||||
<path class="arrow" d="M370 362 H455"/>
|
||||
|
||||
<rect class="card blue" x="455" y="245" width="300" height="235" rx="28"/>
|
||||
<text class="card-title" x="605" y="300">2. Снять только свои</text>
|
||||
<text class="code" x="605" y="360">.off('.orderForm')</text>
|
||||
<text class="card-copy" x="605" y="410">Соседние click-события</text>
|
||||
<text class="card-copy" x="605" y="443">не трогаем.</text>
|
||||
|
||||
<path class="arrow" d="M755 362 H840"/>
|
||||
|
||||
<rect class="card green" x="840" y="245" width="290" height="235" rx="28"/>
|
||||
<text class="card-title" x="985" y="300">3. Назначить один</text>
|
||||
<text class="code" x="985" y="360">.on('click.orderForm')</text>
|
||||
<text class="card-copy" x="985" y="410">Новый обработчик</text>
|
||||
<text class="card-copy" x="985" y="443">предсказуемо один.</text>
|
||||
|
||||
<path class="arrow" d="M600 520 V600"/>
|
||||
<rect x="180" y="620" width="840" height="90" rx="22" fill="#f3f6f9" stroke="#64707d" stroke-width="2"/>
|
||||
<text class="note" x="600" y="660">Проверка: три вызова mount() → одна отправка формы.</text>
|
||||
<text class="small" x="600" y="694">Это свойство функции инициализации, а не удача порядка загрузки.</text>
|
||||
|
||||
<text class="small" x="600" y="755">Схема для статьи о jQuery 3.x и legacy-интерфейсе, май 2018.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 620" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Диагностика JSON-ответа API</title>
|
||||
<desc id="desc">Сырой ответ разбирается функцией json_decode, затем проверяется json_last_error, а при успехе — тип и обязательные поля контракта.</desc>
|
||||
<defs>
|
||||
<linearGradient id="json-bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#173a46"/>
|
||||
<stop offset="1" stop-color="#245f55"/>
|
||||
</linearGradient>
|
||||
<marker id="json-arrow" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
||||
<path d="M0 0 L10 5 L0 10z" fill="#f2d38d"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="1200" height="620" rx="34" fill="url(#json-bg)"/>
|
||||
<text x="72" y="86" fill="#fcf7e8" font-family="Arial, sans-serif" font-size="32" font-weight="700">«null» и ошибка синтаксиса — не один случай</text>
|
||||
<text x="72" y="124" fill="#c2dfd2" font-family="Arial, sans-serif" font-size="19">Сначала проверяем корректность JSON, потом — договор конкретного endpoint</text>
|
||||
<g font-family="Arial, sans-serif">
|
||||
<rect x="68" y="242" width="208" height="122" rx="19" fill="#d4e8ff"/>
|
||||
<text x="112" y="289" fill="#244868" font-size="23" font-weight="700">Сырой ответ</text>
|
||||
<text x="102" y="323" fill="#244868" font-size="17">строка + размер</text>
|
||||
<path d="M276 303 H386" fill="none" stroke="#f2d38d" stroke-width="5" marker-end="url(#json-arrow)"/>
|
||||
<rect x="407" y="242" width="202" height="122" rx="19" fill="#f2d38d"/>
|
||||
<text x="440" y="289" fill="#4a3b21" font-size="23" font-weight="700">json_decode</text>
|
||||
<text x="446" y="323" fill="#4a3b21" font-size="17">без догадок</text>
|
||||
<path d="M609 303 H716" fill="none" stroke="#f2d38d" stroke-width="5" marker-end="url(#json-arrow)"/>
|
||||
<rect x="737" y="242" width="192" height="122" rx="19" fill="#b8e8e0"/>
|
||||
<text x="771" y="289" fill="#204944" font-size="22" font-weight="700">json_last_error</text>
|
||||
<text x="776" y="323" fill="#204944" font-size="17">JSON_ERROR_NONE?</text>
|
||||
<path d="M833 364 V452 H609" fill="none" stroke="#f2d38d" stroke-width="5" marker-end="url(#json-arrow)"/>
|
||||
<text x="780" y="414" fill="#f2d38d" font-size="16">нет</text>
|
||||
<rect x="408" y="449" width="204" height="104" rx="19" fill="#f5c5bb"/>
|
||||
<text x="438" y="491" fill="#61332d" font-size="21" font-weight="700">decode error</text>
|
||||
<text x="431" y="523" fill="#61332d" font-size="16">код + хеш тела</text>
|
||||
<path d="M929 303 H1000" fill="none" stroke="#f2d38d" stroke-width="5" marker-end="url(#json-arrow)"/>
|
||||
<text x="948" y="281" fill="#f2d38d" font-size="16">да</text>
|
||||
<rect x="1021" y="242" width="125" height="122" rx="19" fill="#e2d5ff"/>
|
||||
<text x="1047" y="286" fill="#4c376c" font-size="20" font-weight="700">Контракт</text>
|
||||
<text x="1036" y="320" fill="#4c376c" font-size="16">тип и поля</text>
|
||||
<path d="M1083 364 V452 H1000" fill="none" stroke="#f2d38d" stroke-width="5" marker-end="url(#json-arrow)"/>
|
||||
<rect x="797" y="449" width="204" height="104" rx="19" fill="#f5c5bb"/>
|
||||
<text x="823" y="491" fill="#61332d" font-size="21" font-weight="700">contract error</text>
|
||||
<text x="830" y="523" fill="#61332d" font-size="16">валидный JSON</text>
|
||||
</g>
|
||||
<text x="72" y="584" fill="#c2dfd2" font-family="Arial, sans-serif" font-size="18">Не пишем тело в общий журнал: размер и SHA-256 достаточно сравнить два ответа.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,51 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 620" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Диагностика PHP-ошибки в интеграции</title>
|
||||
<desc id="desc">Контекст операции создаётся перед вызовом партнёра и соединяется с тремя ветками обработки: warning, непойманное исключение и фатальная ошибка при завершении PHP.</desc>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" x2="1" y1="0" y2="1">
|
||||
<stop offset="0" stop-color="#101828"/>
|
||||
<stop offset="1" stop-color="#1d3d4f"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="150%">
|
||||
<feDropShadow dx="0" dy="10" stdDeviation="10" flood-color="#06111a" flood-opacity=".32"/>
|
||||
</filter>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
||||
<path d="M0 0 L10 5 L0 10z" fill="#b8e8e0"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="1200" height="620" rx="34" fill="url(#bg)"/>
|
||||
<text x="72" y="88" fill="#eaf5f4" font-family="Arial, sans-serif" font-size="32" font-weight="700">Контекст не должен появляться после падения</text>
|
||||
<text x="72" y="125" fill="#a9c8c4" font-family="Arial, sans-serif" font-size="19">request ID и этап операции создаются до внешнего вызова</text>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="72" y="215" width="220" height="150" rx="20" fill="#f0b667"/>
|
||||
<text x="104" y="267" fill="#192538" font-family="Arial, sans-serif" font-size="22" font-weight="700">Операция</text>
|
||||
<text x="104" y="302" fill="#192538" font-family="Arial, sans-serif" font-size="18">request_id</text>
|
||||
<text x="104" y="329" fill="#192538" font-family="Arial, sans-serif" font-size="18">stage</text>
|
||||
</g>
|
||||
<path d="M292 290 H395" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<circle cx="418" cy="290" r="15" fill="#b8e8e0"/>
|
||||
<path d="M433 290 H486 V193 H558" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<path d="M433 290 H486 V291 H558" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<path d="M433 290 H486 V389 H558" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="578" y="137" width="255" height="112" rx="18" fill="#d4e8ff"/>
|
||||
<text x="608" y="180" fill="#183653" font-family="Arial, sans-serif" font-size="21" font-weight="700">warning / notice</text>
|
||||
<text x="608" y="214" fill="#183653" font-family="Arial, sans-serif" font-size="17">set_error_handler</text>
|
||||
<rect x="578" y="235" width="255" height="112" rx="18" fill="#d8f1e5"/>
|
||||
<text x="608" y="278" fill="#174133" font-family="Arial, sans-serif" font-size="21" font-weight="700">Throwable</text>
|
||||
<text x="608" y="312" fill="#174133" font-family="Arial, sans-serif" font-size="17">set_exception_handler</text>
|
||||
<rect x="578" y="333" width="255" height="112" rx="18" fill="#f6d9d2"/>
|
||||
<text x="608" y="376" fill="#63342b" font-family="Arial, sans-serif" font-size="21" font-weight="700">fatal error</text>
|
||||
<text x="608" y="410" fill="#63342b" font-family="Arial, sans-serif" font-size="17">shutdown + error_get_last</text>
|
||||
</g>
|
||||
<path d="M833 193 H930 V291 H994" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<path d="M833 291 H994" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<path d="M833 389 H930 V291 H994" fill="none" stroke="#b8e8e0" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="1014" y="215" width="140" height="150" rx="20" fill="#b8e8e0"/>
|
||||
<text x="1044" y="273" fill="#183b3a" font-family="Arial, sans-serif" font-size="22" font-weight="700">Лог</text>
|
||||
<text x="1037" y="307" fill="#183b3a" font-family="Arial, sans-serif" font-size="16">один факт</text>
|
||||
<text x="1034" y="333" fill="#183b3a" font-family="Arial, sans-serif" font-size="16">для разбора</text>
|
||||
</g>
|
||||
<text x="72" y="531" fill="#a9c8c4" font-family="Arial, sans-serif" font-size="18">Не пишем: пароли, токены, полный запрос и полный ответ партнёра.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,76 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Выдача приватного документа через PHP</title>
|
||||
<desc id="desc">Последовательность запроса: браузер, маршрут приложения, база документов, закрытое хранилище, HTTP-ответ.</desc>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#f6f8fc"/>
|
||||
<stop offset="1" stop-color="#edf5f1"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="12" stdDeviation="16" flood-color="#123049" flood-opacity=".11"/>
|
||||
</filter>
|
||||
<marker id="right-arrow" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="10" markerHeight="10" orient="auto">
|
||||
<path d="M 0 1 L 11 6 L 0 11 z" fill="#3f6b78"/>
|
||||
</marker>
|
||||
<marker id="left-arrow" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="10" markerHeight="10" orient="auto">
|
||||
<path d="M 0 1 L 11 6 L 0 11 z" fill="#6c8f9c"/>
|
||||
</marker>
|
||||
<style>
|
||||
.title { font: 700 44px Inter, Arial, sans-serif; fill: #17324a; }
|
||||
.subtitle { font: 400 23px Inter, Arial, sans-serif; fill: #597080; }
|
||||
.actor { font: 700 22px Inter, Arial, sans-serif; fill: #17324a; text-anchor: middle; }
|
||||
.role { font: 600 16px Inter, Arial, sans-serif; fill: #6b7d8d; letter-spacing: 1.4px; text-anchor: middle; }
|
||||
.label { font: 600 18px Inter, Arial, sans-serif; fill: #34566a; }
|
||||
.code { font: 600 17px Menlo, Consolas, monospace; fill: #236779; }
|
||||
.note { font: 400 18px Inter, Arial, sans-serif; fill: #466174; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect width="1600" height="900" fill="url(#bg)"/>
|
||||
<text x="104" y="110" class="title">Приватный файл: доступ проверяется до чтения с диска</text>
|
||||
<text x="104" y="152" class="subtitle">URL содержит ID записи. Настоящий ключ и путь остаются за маршрутом приложения.</text>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="112" y="238" width="236" height="108" rx="22" fill="#ffffff"/>
|
||||
<text x="230" y="282" class="actor">Браузер</text>
|
||||
<text x="230" y="314" class="role">ПОЛЬЗОВАТЕЛЬ</text>
|
||||
|
||||
<rect x="466" y="238" width="250" height="108" rx="22" fill="#ffffff"/>
|
||||
<text x="591" y="282" class="actor">Маршрут PHP</text>
|
||||
<text x="591" y="314" class="role">АВТОРИЗАЦИЯ</text>
|
||||
|
||||
<rect x="837" y="238" width="248" height="108" rx="22" fill="#ffffff"/>
|
||||
<text x="961" y="282" class="actor">documents</text>
|
||||
<text x="961" y="314" class="role">БАЗА ДАННЫХ</text>
|
||||
|
||||
<rect x="1210" y="238" width="280" height="108" rx="22" fill="#ffffff"/>
|
||||
<text x="1350" y="282" class="actor">Закрытый каталог</text>
|
||||
<text x="1350" y="314" class="role">ФАЙЛОВАЯ СИСТЕМА</text>
|
||||
</g>
|
||||
|
||||
<line x1="230" y1="346" x2="230" y2="708" stroke="#b8cbd5" stroke-width="3" stroke-dasharray="9 10"/>
|
||||
<line x1="591" y1="346" x2="591" y2="708" stroke="#b8cbd5" stroke-width="3" stroke-dasharray="9 10"/>
|
||||
<line x1="961" y1="346" x2="961" y2="708" stroke="#b8cbd5" stroke-width="3" stroke-dasharray="9 10"/>
|
||||
<line x1="1350" y1="346" x2="1350" y2="708" stroke="#b8cbd5" stroke-width="3" stroke-dasharray="9 10"/>
|
||||
|
||||
<path d="M230 414 H580" fill="none" stroke="#3f6b78" stroke-width="6" stroke-linecap="round" marker-end="url(#right-arrow)"/>
|
||||
<text x="308" y="398" class="label">1. GET</text>
|
||||
<text x="308" y="430" class="code">/documents/42/download</text>
|
||||
|
||||
<path d="M591 490 H950" fill="none" stroke="#3f6b78" stroke-width="6" stroke-linecap="round" marker-end="url(#right-arrow)"/>
|
||||
<text x="684" y="474" class="label">2. Проверка владельца</text>
|
||||
<text x="684" y="506" class="code">id + owner_id + status</text>
|
||||
|
||||
<path d="M961 562 H1339" fill="none" stroke="#3f6b78" stroke-width="6" stroke-linecap="round" marker-end="url(#right-arrow)"/>
|
||||
<text x="1069" y="546" class="label">3. Найден ключ</text>
|
||||
<text x="1069" y="578" class="code">storage_key.pdf</text>
|
||||
|
||||
<path d="M1339 634 H602" fill="none" stroke="#6c8f9c" stroke-width="6" stroke-linecap="round" marker-end="url(#left-arrow)"/>
|
||||
<text x="1000" y="618" class="label">4. PHP читает только закрытый путь</text>
|
||||
|
||||
<path d="M580 706 H241" fill="none" stroke="#27846e" stroke-width="7" stroke-linecap="round" marker-end="url(#left-arrow)"/>
|
||||
<text x="314" y="689" class="label">5. HTTP-ответ</text>
|
||||
<text x="314" y="724" class="code">Content-Type: application/pdf</text>
|
||||
|
||||
<rect x="112" y="777" width="1378" height="66" rx="18" fill="#ffffff" stroke="#c9dcda" stroke-width="2"/>
|
||||
<text x="146" y="818" class="note">Прямого URL к каталогу нет: запись в базе связывает пользователя с ключом, а маршрут решает, можно ли читать файл.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,91 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Контракт безопасной загрузки аватара</title>
|
||||
<desc id="desc">Схема из четырёх шагов: браузер, временный файл PHP, проверка, закрытое хранилище.</desc>
|
||||
<defs>
|
||||
<linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#f6f8fc"/>
|
||||
<stop offset="1" stop-color="#eef4f3"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="14" stdDeviation="18" flood-color="#123049" flood-opacity=".12"/>
|
||||
</filter>
|
||||
<marker id="arrow" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="10" markerHeight="10" orient="auto">
|
||||
<path d="M 0 1 L 11 6 L 0 11 z" fill="#6b7d8d"/>
|
||||
</marker>
|
||||
<style>
|
||||
.title { font: 700 44px Inter, Arial, sans-serif; fill: #17324a; }
|
||||
.subtitle { font: 400 23px Inter, Arial, sans-serif; fill: #597080; }
|
||||
.step { font: 700 18px Inter, Arial, sans-serif; fill: #75909f; letter-spacing: 1.8px; }
|
||||
.card-title { font: 700 28px Inter, Arial, sans-serif; fill: #17324a; }
|
||||
.card-text { font: 400 20px Inter, Arial, sans-serif; fill: #466174; }
|
||||
.code { font: 600 18px Menlo, Consolas, monospace; fill: #1f6672; }
|
||||
.badge { font: 700 16px Inter, Arial, sans-serif; fill: #2e5267; }
|
||||
.note { font: 600 18px Inter, Arial, sans-serif; fill: #36536a; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect width="1600" height="900" fill="url(#background)"/>
|
||||
<path d="M0 684 C260 613 404 779 643 692 S1139 590 1600 717 V900 H0Z" fill="#e4f0ef"/>
|
||||
<text x="106" y="116" class="title">Загрузка аватара: решение принимается до переноса</text>
|
||||
<text x="106" y="158" class="subtitle">Клиент присылает файл. Приложение выбирает допустимый тип, размеры и собственный ключ.</text>
|
||||
|
||||
<line x1="390" y1="435" x2="481" y2="435" stroke="#6b7d8d" stroke-width="7" stroke-linecap="round" marker-end="url(#arrow)"/>
|
||||
<line x1="765" y1="435" x2="856" y2="435" stroke="#6b7d8d" stroke-width="7" stroke-linecap="round" marker-end="url(#arrow)"/>
|
||||
<line x1="1140" y1="435" x2="1231" y2="435" stroke="#6b7d8d" stroke-width="7" stroke-linecap="round" marker-end="url(#arrow)"/>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="106" y="286" width="284" height="302" rx="24" fill="#ffffff"/>
|
||||
<rect x="106" y="286" width="284" height="12" rx="6" fill="#ea8847"/>
|
||||
<circle cx="154" cy="344" r="25" fill="#fce3d1"/>
|
||||
<text x="146" y="351" class="badge" fill="#c8692b">1</text>
|
||||
<text x="194" y="351" class="step">КЛИЕНТ</text>
|
||||
<text x="142" y="412" class="card-title">Форма</text>
|
||||
<text x="142" y="452" class="code">name=avatar</text>
|
||||
<text x="142" y="496" class="card-text">имя и Content-Type</text>
|
||||
<text x="142" y="526" class="card-text">остаются входом,</text>
|
||||
<text x="142" y="556" class="card-text">а не решением.</text>
|
||||
</g>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="481" y="286" width="284" height="302" rx="24" fill="#ffffff"/>
|
||||
<rect x="481" y="286" width="284" height="12" rx="6" fill="#6994c6"/>
|
||||
<circle cx="529" cy="344" r="25" fill="#dceafa"/>
|
||||
<text x="521" y="351" class="badge" fill="#4374af">2</text>
|
||||
<text x="569" y="351" class="step">PHP</text>
|
||||
<text x="517" y="412" class="card-title">Временный файл</text>
|
||||
<text x="517" y="452" class="code">$_FILES</text>
|
||||
<text x="517" y="496" class="card-text">error · size · tmp_name</text>
|
||||
<text x="517" y="526" class="card-text">Сначала убеждаемся,</text>
|
||||
<text x="517" y="556" class="card-text">что доставка завершилась.</text>
|
||||
</g>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="856" y="286" width="284" height="302" rx="24" fill="#ffffff"/>
|
||||
<rect x="856" y="286" width="284" height="12" rx="6" fill="#4eaa93"/>
|
||||
<circle cx="904" cy="344" r="25" fill="#d6f1e8"/>
|
||||
<text x="896" y="351" class="badge" fill="#27846e">3</text>
|
||||
<text x="944" y="351" class="step">ПРОВЕРКА</text>
|
||||
<text x="892" y="412" class="card-title">Контракт</text>
|
||||
<text x="892" y="452" class="code">finfo_file()</text>
|
||||
<text x="892" y="496" class="card-text">JPEG / PNG · 2 МБ</text>
|
||||
<text x="892" y="526" class="card-text">размеры изображения</text>
|
||||
<text x="892" y="556" class="card-text">и белый список.</text>
|
||||
</g>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="1231" y="286" width="284" height="302" rx="24" fill="#ffffff"/>
|
||||
<rect x="1231" y="286" width="284" height="12" rx="6" fill="#5d7f90"/>
|
||||
<circle cx="1279" cy="344" r="25" fill="#dce8ed"/>
|
||||
<text x="1271" y="351" class="badge" fill="#39586b">4</text>
|
||||
<text x="1319" y="351" class="step">ХРАНИЛИЩЕ</text>
|
||||
<text x="1267" y="412" class="card-title">Закрытый каталог</text>
|
||||
<text x="1267" y="452" class="code">7f4a...c2.png</text>
|
||||
<text x="1267" y="496" class="card-text">свой ключ приложения,</text>
|
||||
<text x="1267" y="526" class="card-text">никакого пути из</text>
|
||||
<text x="1267" y="556" class="card-text">исходного имени.</text>
|
||||
</g>
|
||||
|
||||
<rect x="106" y="688" width="1409" height="88" rx="20" fill="#ffffff" stroke="#c9dcda" stroke-width="2"/>
|
||||
<circle cx="158" cy="732" r="18" fill="#4eaa93"/>
|
||||
<path d="M149 732 l7 7 l13 -16" fill="none" stroke="#ffffff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="202" y="740" class="note">Перенос происходит только после проверки: из браузера в каталог попадает не «файл с именем», а запись, соответствующая контракту.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,86 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Границы доверия при загрузке файла</title>
|
||||
<desc id="desc">Диаграмма показывает клиентские метаданные, результат PHP, Fileinfo и решение приложения.</desc>
|
||||
<defs>
|
||||
<linearGradient id="panel" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#ffffff"/>
|
||||
<stop offset="1" stop-color="#f5f8fb"/>
|
||||
</linearGradient>
|
||||
<filter id="soft-shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="12" stdDeviation="16" flood-color="#142b3d" flood-opacity=".10"/>
|
||||
</filter>
|
||||
<marker id="flow-arrow" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="10" markerHeight="10" orient="auto">
|
||||
<path d="M 0 1 L 11 6 L 0 11 z" fill="#47687b"/>
|
||||
</marker>
|
||||
<style>
|
||||
.title { font: 700 44px Inter, Arial, sans-serif; fill: #17324a; }
|
||||
.subtitle { font: 400 23px Inter, Arial, sans-serif; fill: #597080; }
|
||||
.lane { font: 700 17px Inter, Arial, sans-serif; letter-spacing: 2.2px; }
|
||||
.box-title { font: 700 27px Inter, Arial, sans-serif; fill: #17324a; }
|
||||
.box-copy { font: 400 20px Inter, Arial, sans-serif; fill: #466174; }
|
||||
.mono { font: 600 18px Menlo, Consolas, monospace; fill: #2f6174; }
|
||||
.small { font: 600 16px Inter, Arial, sans-serif; fill: #6b7d8d; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect width="1600" height="900" fill="#f5f8fb"/>
|
||||
<rect x="0" y="0" width="500" height="900" fill="#fff4ed"/>
|
||||
<rect x="500" y="0" width="520" height="900" fill="#eef5fb"/>
|
||||
<rect x="1020" y="0" width="580" height="900" fill="#eef8f4"/>
|
||||
<text x="94" y="104" class="title">Загрузка — это не один сигнал, а несколько границ</text>
|
||||
<text x="94" y="146" class="subtitle">Слева — то, что заявил клиент. Справа — то, на чём приложение строит решение.</text>
|
||||
|
||||
<text x="106" y="226" class="lane" fill="#c5682d">СВЕДЕНИЯ КЛИЕНТА</text>
|
||||
<text x="605" y="226" class="lane" fill="#4579a8">РЕЗУЛЬТАТ PHP</text>
|
||||
<text x="1125" y="226" class="lane" fill="#27846e">РЕШЕНИЕ ПРИЛОЖЕНИЯ</text>
|
||||
|
||||
<line x1="500" y1="184" x2="500" y2="774" stroke="#d78253" stroke-width="3" stroke-dasharray="10 12"/>
|
||||
<line x1="1020" y1="184" x2="1020" y2="774" stroke="#65a18f" stroke-width="3" stroke-dasharray="10 12"/>
|
||||
|
||||
<g filter="url(#soft-shadow)">
|
||||
<rect x="106" y="278" width="300" height="130" rx="20" fill="url(#panel)"/>
|
||||
<text x="138" y="326" class="box-title">Имя и расширение</text>
|
||||
<text x="138" y="364" class="mono">avatar.jpg</text>
|
||||
<text x="138" y="394" class="box-copy">Удобны для интерфейса.</text>
|
||||
|
||||
<rect x="106" y="452" width="300" height="130" rx="20" fill="url(#panel)"/>
|
||||
<text x="138" y="500" class="box-title">Content-Type</text>
|
||||
<text x="138" y="538" class="mono">image/jpeg</text>
|
||||
<text x="138" y="568" class="box-copy">Это поле multipart-части.</text>
|
||||
</g>
|
||||
|
||||
<g filter="url(#soft-shadow)">
|
||||
<rect x="606" y="278" width="310" height="130" rx="20" fill="url(#panel)"/>
|
||||
<text x="638" y="326" class="box-title">Доставка</text>
|
||||
<text x="638" y="364" class="mono">UPLOAD_ERR_OK</text>
|
||||
<text x="638" y="394" class="box-copy">PHP принял файл целиком.</text>
|
||||
|
||||
<rect x="606" y="452" width="310" height="130" rx="20" fill="url(#panel)"/>
|
||||
<text x="638" y="500" class="box-title">Временный файл</text>
|
||||
<text x="638" y="538" class="mono">tmp_name · size</text>
|
||||
<text x="638" y="568" class="box-copy">Есть что проверять на сервере.</text>
|
||||
</g>
|
||||
|
||||
<g filter="url(#soft-shadow)">
|
||||
<rect x="1126" y="278" width="354" height="130" rx="20" fill="url(#panel)"/>
|
||||
<text x="1158" y="326" class="box-title">Fileinfo</text>
|
||||
<text x="1158" y="364" class="mono">finfo_file()</text>
|
||||
<text x="1158" y="394" class="box-copy">Определяет тип временного файла.</text>
|
||||
|
||||
<rect x="1126" y="452" width="354" height="130" rx="20" fill="url(#panel)"/>
|
||||
<text x="1158" y="500" class="box-title">Белый список</text>
|
||||
<text x="1158" y="538" class="mono">JPEG / PNG / 2 МБ</text>
|
||||
<text x="1158" y="568" class="box-copy">Правило конкретного сценария.</text>
|
||||
</g>
|
||||
|
||||
<path d="M406 343 C475 343 516 343 606 343" fill="none" stroke="#d78253" stroke-width="6" stroke-linecap="round" marker-end="url(#flow-arrow)"/>
|
||||
<path d="M406 517 C480 517 532 517 606 517" fill="none" stroke="#d78253" stroke-width="6" stroke-linecap="round" marker-end="url(#flow-arrow)"/>
|
||||
<path d="M916 343 C985 343 1051 343 1126 343" fill="none" stroke="#4e8ab8" stroke-width="6" stroke-linecap="round" marker-end="url(#flow-arrow)"/>
|
||||
<path d="M916 517 C985 517 1051 517 1126 517" fill="none" stroke="#4e8ab8" stroke-width="6" stroke-linecap="round" marker-end="url(#flow-arrow)"/>
|
||||
|
||||
<rect x="106" y="665" width="1374" height="102" rx="22" fill="#ffffff" stroke="#cfdfd9" stroke-width="2"/>
|
||||
<circle cx="156" cy="716" r="19" fill="#27846e"/>
|
||||
<path d="M147 716 l7 7 l14 -17" fill="none" stroke="#ffffff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="203" y="708" class="box-title">Практическое правило</text>
|
||||
<text x="203" y="742" class="box-copy">Имя и клиентский MIME-тип не определяют допуск. Они становятся полезными только после того, как серверный анализ и белый список дали ответ.</text>
|
||||
<text x="106" y="829" class="small">Отдельная проверка размеров дополняет контракт изображения, но не заменяет проверку типа.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |