---
name: ocmod-writing
description: "Практическое руководство по написанию OCMOD XML с учётом однострочного поиска и особенностей OpenCart."
---

# OCMOD — как писать корректные XML-патчи

Цель: дать краткие, проверяемые правила и примеры для создания OCMOD-файлов, которые надёжно применяются в OpenCart.

1) Коротко о формате
- Основная структура:

```
<modification>
  <name>...</name>
  <code>unique_code</code>
  <version>1.0</version>
  <author>Author</author>
  <file path="catalog/.../file.php">
    <operation>
      <search><![CDATA[...]]></search>
      <add position="replace|before|after"><![CDATA[...]]></add>
    </operation>
  </file>
</modification>
```

2) Однострочный поиск — ключевое ограничение
- OpenCart OCMOD выполняет поиск по строкам: содержимое `<search>` должно соответствовать одной строке в исходном файле. Это значит:
  - Не пытайтесь искать многострочные фрагменты в `<search>` — они не сработают.
  - Если целевой код может быть в одной строке с разным количеством пробелов, используйте регулярные выражения (см. пункт 3) или пишите поиск с учётом возможных пробелов.

3) Регулярные выражения
- Если нужен гибкий поиск, используйте `regex="true"` в элементе `<search>` (если платформа поддерживает):

```
<search regex="true"><![CDATA[\$this->response->setOutput\(\$this->load->view\('product/product', \$data\)\);]]></search>
```

- При использовании regex:
  - Экранируйте слэши и специальные символы.
  - Для пробелов используйте `\s+` или `\s*`.
  - Ограничьте регулярное выражение так, чтобы оно оставалось однострочным.

4) CDATA и вставляемый код
- Оборачивайте код в `<![CDATA[ ... ]]>`, чтобы не ломать XML-структуру при наличии символов `<`, `&` и т.п.
- Пример вставки:

```
<add position="after"><![CDATA[
  // Дополнительный вызов логирования
  $this->log->write('product view');
]]></add>
```

5) Позиции вставки
- `position="before"` — вставляет перед найденной строкой.
- `position="after"` — вставляет после найденной строки.
- `position="replace"` — заменяет найденную строку полностью.

6) Практические рекомендации
- Сначала найдите точную строку в исходном файле (или минимально-уникальную часть строки) и копируйте её в `<search>`.
- Если строка генерируется сборщиком/минифицирована (всё в одной строке), используйте regex с `\s*` и жёсткими якорями, например `^` и `$`.
- Для PHP-кода используйте контекстные вызовы (например, строка с `$this->response->setOutput(...)`), а не части выражения.
- Добавляйте `<![CDATA[]]>` вокруг вставляемого PHP/HTML.
- Тестируйте OCMOD локально: положите XML в `dev-modules/<имя_модуля>/upload/system/` или `system/` и выполните установку через админку или `ocm install`, затем Refresh модификаций.

7) Проверка и откат
- Обновите Modifications → Refresh и очистите кеш шаблонов/кэша.
- При ошибках удалите OCMOD через админку и восстановите файлы из репозитория (или пересоздайте). Держите резервную копию изменяемых файлов.

8) Метаданные и уникальность
- Поле `<code>` должно быть уникальным для модификации — это облегчает поиск и откат.
- Используйте префикс: `автор_название_модуля`. Избегайте коротких имён вроде `test` или `fix`.
- Всегда указывайте `<version>` и `<link>` — это помогает при поддержке через время.

9) Стратегия поиска и атрибуты `<search>`
- **Уникальность**: ищите максимально специфичный фрагмент, а не `<?php` или `</div>`.
- **Атрибут `index`**: если нужная строка встречается несколько раз, используйте `<search index="2">` (отсчёт с нуля).
- **Атрибут `trim="true"`**: всегда добавляйте — пробелы/табуляция в начале/конце строки не будут ломать поиск.
- **Минимум кода в поиске**: чем длиннее строка поиска, тем выше шанс, что другой модуль её изменит и поиск провалится.

10) Стратегия вставки (`<add>`)
- Предпочитайте `position="before"` или `position="after"` вместо `position="replace"` — полная замена является главной причиной конфликтов.
- Если вставляете много кода, вынесите логику в отдельный контроллер или helper-метод, а в OCMOD лишь вызовите его одной строкой.

11) Порядок применения и зависимости
- Модификаторы применяются в алфавитном порядке по `<code>`. Если ваш модуль зависит от другого, убедитесь, что ваш `<code>` идёт позже.
- Если проект использует и VQMod, и OCMOD: VQMod применяется **первым**, OCMOD работает уже с его результатом — учитывайте это при поиске строк.

12) Чек-лист безопасного разработчика
- После каждого «Refresh» модификаций заглядывайте во вкладку **Logs** в админке. Надпись `NOT FOUND!` означает, что модификатор не применился.
- **Никогда не редактируйте** файлы в `system/storage/modification/` — они исчезнут после следующего «Refresh».
- Если правку можно реализовать через **Events** (события) OpenCart 3 — используйте события. OCMOD — крайняя мера для мест, где события не предусмотрены.

13) Пример идеальной операции
```xml
<operation error="skip">
  <search trim="true"><![CDATA[$data['footer'] = $this->load->controller('common/footer');]]></search>
  <add position="after"><![CDATA[$data['my_custom_var'] = 'Hello World';]]></add>
</operation>
```

14) Частые ошибки и как их избежать
- Поиск не сработал из-за пробелов/табуляции — используйте `trim="true"` или `\s` в regex.
- Поиск не сработал, потому что строка была объединена в одну после минификации — используйте regex или поместите поиск в месте, где код гарантированно имеет стабильный вид.
- Не уникальная строка поиска — добавьте `index="N"` для выбора нужного вхождения.
- Использован `position="replace"`, конфликтующий с другим модулем — переходите на `before`/`after`.
- Забыл CDATA — XML сломается при вставке `<?php` или `<`.
- Неправильный regex — «роняет» генерацию кэша модификаторов; всегда тестируйте локально.
- Слишком длинная строка поиска — другой модуль изменил её, и ваш патч не нашёл совпадений.

15) Примеры
- Простой replace (однострочный поиск):

```
<file path="catalog/controller/product/product.php">
  <operation>
    <search><![CDATA[$this->response->setOutput($this->load->view('product/product', $data));]]></search>
    <add position="replace"><![CDATA[// модифицированный вывод
$this->response->setOutput($this->load->view('product/product', $data));
]]></add>
  </operation>
</file>
```

- Regex-пример с гибкими пробелами:

```
<file path="catalog/controller/checkout/checkout.php">
  <operation>
    <search regex="true"><![CDATA[\$this->session->data\['order_id'\];]]></search>
    <add position="after"><![CDATA[
      // логирование номера заказа
      $this->log->write('Order: ' . $this->session->data['order_id']);
    ]]></add>
  </operation>
</file>
```

16) Размещение и установка
- OCMOD можно хранить в модуле: `dev-modules/<имя_модуля>/upload/system/<имя>.ocmod.xml` или положить в `system/`.
- После развертывания выполните `ocm install` (если доступна) или загрузите XML через админку и выполните Refresh модификаций.

Если хотите, добавлю шаблон OCMOD XML-файла в репозиторий или генератор шаблонов для модулей.
