مقدمة صغيرة:
يعد تنسيق ocmod حلاً أنيقًا جدًا لتعريف التعديلات على الملفات المصدر ، بغض النظر عن تنسيقها. أحد أجزاء التنسيق هو ملف XML ، والذي يحدد في أي ملف ومكان في هذا الملف يجب إجراء تغييرات معينة. فيما يلي مثال لملف ocmod (مأخوذ من ocmod.net ، يمكن العثور على وصف أكثر تفصيلاً هناك):
<?xml version="1.0" encoding="utf-8"?>
<modification>
<name> </name>
<code>product-page-views</code>
<version>1.0</version>
<author>https://ocmod.net</author>
<link>https://ocmod.net</link>
<file path="catalog/controller/product/product.php">
<operation>
<search>
<![CDATA[$data['images'] = array();]]>
</search>
<add position="after">
<![CDATA[
$data['view'] = $product_info['viewed'];
]]>
</add>
</operation>
</file>
<file path="catalog/language/en-gb/product/product.php">
<operation>
<search>
<![CDATA[$_['text_search']]]>
</search>
<add position="before">
<![CDATA[
$_['text_view'] = 'View: ';
]]>
</add>
</operation>
</file>
</modification>
بشكل عام ، نضع ما يلي:
<file path=" ">
<operation>
<search><![CDATA[ ]]></search>
<add position=" – , ">
<![CDATA[ ]]>
</add>
</operation>
</file>
على الرغم من أن المبدأ شفاف تمامًا ، إلا أن السؤال الذي يطرح نفسه: هل من الممكن أتمتة عملية إنشائه أم سيكون من الضروري كتابته يدويًا ، لأنه يجب أن يشارك المبرمج في البرمجة ، وليس
من الناحية المثالية ، ستبدو كتابة تعديل لـ Opencart على النحو التالي: لقد قمنا بتنزيل الإصدار "النظيف" من المتجر ، وقمنا ببعض التغييرات مباشرةً في كود المصدر الخاص به وقمنا بتشغيل البرنامج النصي "السحري" الذي أنشأ ocmod بالكامل على الفور. في الواقع ، كل شيء أكثر تعقيدًا بعض الشيء ، لكننا سنحاول الاقتراب من هذا المخطط. تكمن المشكلة الأساسية في تحديد الموقع المراد إدراجه في الملف (ما بين <search> ... </search>). يجب على المبرمج القيام بذلك. كقاعدة عامة ، يحاولون جعله عالميًا قدر الإمكان من أجل تغطية المزيد من الإصدارات المحتملة من المصدر ، وفي نفس الوقت بحيث يتغير المكان المطلوب بالضبط. من الواضح أن هذا مصنوع يدويًا. كل شيء آخر مؤتمت.
استطراد صغير: يحدث البحث في السلسلة بأكملها ، ولا يمكن الإدراج إلا قبلها أو بعدها أو بدلاً منها ، ولكن ليس في الداخل (في حزمة OCMOD الكلاسيكية لـ Opencart). هذا قيد غير مفهوم بالنسبة لي شخصيًا. أيضًا ، لا أفهم سبب استحالة تعيين العديد من علامات <search> للعثور على نقطة الإدراج المطلوبة ، والتي ستتم معالجتها باستمرار - بعد كل شيء ، سيكون البحث أكثر مرونة. على سبيل المثال ، إذا وجدت في كود PHP ، على سبيل المثال ، اسم الوظيفة ، ثم ابحث عن المكان المناسب فيها ، أو بطريقة أخرى حسب تقدير المبرمج. لكنني لم أجد هذا ، إذا كنت مخطئًا ، يرجى تصحيحه.
والآن أهم شيء: يمكنك أتمتة عملية إنشاء ملف ocmod ، وتحتاج فقط إلى الالتزام بالمخطط المطلوب. أولاً ، في الملف المصدر ، نحتاج إلى تحديد مكان تغييراتنا بطريقة ما - سواء للطلب فقط ، أو حتى يعرف مولد ocmod كل شيء بطريقة قابلة للتوجيه. لنفترض أن اسمنا بيتر نيكولايفيتش إيفانوف (الصدف عشوائية). دعنا نرفق جميع التغييرات بين علامات <PNI> ... </PNI> ، وحتى لا تفسد العلامات المصدر ، سنضع هذه العلامات في تعليقات اللغة التي نعمل عليها حاليًا. بين العلامات ، في مكانها الصحيح ، قم بتعيين سلسلة البحث بين <search> </search> والشفرة المضافة بين <add> </add>. سيكون أوضح بمثال:
للتغييرات في PHP:
…
( opencart)
…
// <PNI>
// -
// , ( )
// <search> public function index() {</search>
// <add position=”after”>
$x = 5;
$y = 6;
//</add> </PNI>
…
او مثل هذا:
…
( opencart)
…
/* <PNI>
<search> public function index() {</search>
<add position=”after”> */
$x = 5;
$y = 6;
/*</add> </PNI> */
…
إذا كان لدى <search> أو <add> أية سمات ، على سبيل المثال ، <search index = ”1”> ، فسيتم نقلها "كما هي" إلى ملف ocmod. ما نكتبه بينهما لا يتطلب أي هروب لـ XML ، نحن فقط نكتب سلسلة البحث والرمز.
مثال آخر ، بالفعل لملف twig الذي نقوم بتعديله:
{# <PNI>
<search><li><span style="text-decoration: line-through;">{{ price }}</span></li></search>
<add position="replace">
#}
<li><span class="combination-base-price" style="text-decoration: line-through;">{{ price }}</span></li>
{# </add></PNI> #}
بعد أن نصمم كل التغييرات التي أجريناها بهذه الطريقة ، نحتاج إلى نص يتعامل مع كل هذا ، بالإضافة إلى أرشيفي. أشارككم إصداري: يتكون من ملف تكوين ونص بنفسه.
ملف التكوين make-ocmod.opencart.local.cfg.php (ترميز UTF-8 ، هذا مثال ، الجميع يفعل ذلك بأنفسهم):
<?php
define("ROOT_PATH", "../../opencart.local");
define("ENCODING", "utf-8");
define("NAME", " ocmod");
define("CODE", "product-page-views");
define("VERSION", "1.0");
define("AUTHOR", "AS");
define("LINK", "");
define("TAG_OPERATION_BEGIN", "<PNI>");
define("TAG_OPERATION_END", "</PNI>");
define("TAG_SEARCH_BEGIN", "<search"); // !! >
define("TAG_SEARCH_END", "</search>");
define("TAG_ADD_BEGIN", "<add"); // !! >
define("TAG_ADD_END", "</add>");
// </add>
// ( , , ).
// , ,
// </add> ( , \t, \r, \n , )
$commentsBegin = [ '//', '/*', '<!--', '{#' ];
// <add>
// ( , , ).
// , ,
// <add> ( , \t, \r, \n , )
$commentsEnd = [ '*/', '-->', '#}' ];
// ,
// .
$exclude = [ '/cache/', '/cache-/' ];
// upload.
// , .
$upload = [
'admin/view/stylesheet/combined-options.css',
'admin/view/javascript/combined-options.js',
'catalog/view/theme/default/stylesheet/combined-options.css',
'admin/view/image/combined-options/cross.png',
'catalog/view/javascript/combined-options/combined.js',
'catalog/view/javascript/combined-options/aswmultiselect.js',
'admin/view/image/combined-options/select.png'
];
// install.sql.
// ( Opencart )
$sql = "";
?>
الآن الشيء الرئيسي هو مولد ملفات ocmod xml.
نص Make-ocmod.php (ترميز UTF-8):
<?php
include_once ($argv[1]);
function processFile($fileName, $relativePath) {
global $commentsBegin, $commentsEnd, $xml, $exclude;
if ($exclude)
foreach ($exclude as $ex)
if (false !== strpos($relativePath, $ex))
return;
$text = file_get_contents($fileName);
$end = -1;
while (false !== ($begin = strpos($text, TAG_OPERATION_BEGIN, $end + 1))) {
$end = strpos($text, TAG_OPERATION_END, $begin + 1);
if (false === $end)
die ("No close operation tag in ".$fileName);
$search = false;
$searchEnd = $begin;
while (false !== ($searchBegin = strpos($text, TAG_SEARCH_BEGIN, $searchEnd + 1)) and $searchBegin < $end) {
$searchBeginR = strpos($text, '>', $searchBegin + 1);
$searchAttributes = substr($text, $searchBegin + strlen(TAG_SEARCH_BEGIN), $searchBeginR - $searchBegin - strlen(TAG_SEARCH_BEGIN));
if (false === $searchBeginR or $searchBeginR >= $end)
die ("Invalid search tag in ".$fileName);
$searchEnd = strpos($text, TAG_SEARCH_END, $searchBeginR + 1);
if (false === $searchEnd or $searchEnd >= $end)
die ("No close search tag in ".$fileName);
//
$search = substr($text, $searchBeginR + 1, $searchEnd - $searchBeginR - 1);
}
$addBegin = strpos($text, TAG_ADD_BEGIN, $begin + 1);
if (false === $addBegin or $addBegin >= $end)
die ("No begin add tag in ".$fileName);
$addBeginR = strpos($text, '>', $addBegin + 1);
$addAttributes = substr($text, $addBegin + strlen(TAG_ADD_BEGIN), $addBeginR - $addBegin - strlen(TAG_ADD_BEGIN));
if (false === $addBeginR or $addBeginR >= $end)
die ("Invalid add tag in ".$fileName);
$addEnd = strpos($text, TAG_ADD_END, $addBeginR + 1);
if (false === $addEnd or $addEnd >= $end)
die ("No close add tag in ".$fileName);
$codeBegin = $addBeginR + 1;
$codeEnd = $addEnd;
// ,
// - . .
$p = $codeBegin;
while (@$text[$p] === " " or @$text[$p] === "\t" or @$text[$p] === "\r" or @$text[$p] === "\n")
$p ++;
if ($p < $addEnd) {
foreach ($commentsEnd as $tag)
if (substr($text, $p, strlen($tag)) === $tag)
$codeBegin = $p + strlen($tag);
}
$p = $codeEnd - 1;
while (@$text[$p] === " " or @$text[$p] === "\t" or @$text[$p] === "\r" or @$text[$p] === "\n")
$p --;
if ($p >= $codeBegin) {
foreach ($commentsBegin as $tag)
if (substr($text, $p - strlen($tag) + 1, strlen($tag)) === $tag)
$codeEnd = $p - strlen($tag) + 1;
}
$code = substr($text, $codeBegin, $codeEnd - $codeBegin - 1);
$xml .= "
<file path=\"".str_replace('"', '\"', $relativePath)."\">
<operation>".(false !== $search ? "
<search{$searchAttributes}>
<![CDATA[{$search}]]>
</search>" : "")."
<add{$addAttributes}>
<![CDATA[{$code}]]>
</add>
</operation>
</file>";
}
}
function processDir($dir, $relativePath = '') {
global $exclude;
$cdir = scandir($dir);
foreach ($cdir as $key => $value) {
if (!in_array($value,array(".", ".."))) {
$fileName = $dir . DIRECTORY_SEPARATOR . $value;
$newRelativePath = ($relativePath ? $relativePath.'/' : '').$value;
$excluded = false;
if ($exclude)
foreach ($exclude as $ex)
$excluded = $excluded or false !== strpos($newRelativePath, $ex);
if ($excluded)
continue;
if (is_dir($fileName)) {
processDir($fileName, $newRelativePath);
} else {
processFile($fileName, $newRelativePath);
}
}
}
}
function delTree($dir, $delRoot = false) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? delTree("$dir/$file", true) : unlink("$dir/$file");
}
return $delRoot ? rmdir($dir) : true;
}
$xml = "<?xml version=\"1.0\" encoding=\"".ENCODING."\"?>
<modification>
<name>".NAME."</name>
<code>".CODE."</code>
<version>".VERSION."</version>
<author>".AUTHOR."</author>
<link>".LINK."</link>";
processDir(ROOT_PATH);
$xml .= "
</modification>";
file_put_contents('publish/install.xml', $xml);
file_put_contents('publish/install.sql', $sql);
delTree('publish/upload');
foreach ($upload as $file) {
$srcfile = ROOT_PATH.(@$file[0] === '/' ? '' : '/').$file;
$dstfile = 'publish/upload'.(@$file[0] === '/' ? '' : '/').$file;
mkdir(dirname($dstfile), 0777, true);
copy($srcfile, $dstfile);
}
?>
سطر الأوامر make-ocmod.cmd الذي يقوم بتشغيل كل هذا:
del /f/q/s publish.ocmod.zip
php make-ocmod.php make-ocmod.opencart.local.cfg.php
cd publish
..\7z.exe a -r -tZip ..\publish.ocmod.zip *.*
أنا أستخدم 7zip ، لذا يجب أن يكون 7z.exe في نفس المكان مثل سطر الأوامر. يمكن لأي شخص يريد استخدامه تنزيله من https://www.7-zip.org/ .
هذا هو مدير الأوامر لنظام Windows. أعتقد أن من على لينكس سيعيد الكتابة دون مشاكل.
ملخص: في رأيي ، من الأسهل بكثير العمل بهذه الطريقة بدلاً من تعديل ocmod يدويًا في كل مرة. عندما نضيف رمزًا ، نضع علامات البحث الخاصة بنا لهذا الجزء من الكود على الفور ، ثم نركز فقط على عملنا. لم نعد نهتم ببنية ملف xml ، ونجري أي تصحيح لتعديلنا على الفور ، ونتحقق من عمله فورًا ثم ننشئ ملف ocmod جديدًا بنقرة واحدة.