Ocmod file generator لمتجر عبر الإنترنت على Opencart

هل من الواقعي التركيز على الخوارزميات الخاصة بك عند تطوير تعديلات لمحرك متجر Opencart الشهير على الإنترنت ، وترك إعداد ملف للتحميل إلى CMS تحت رحمة البرامج النصية الخاصة؟ في الواقع ، هذا ما سيجعل الحياة أسهل بكثير للمطورين في ظل Opencart ، وفي هذه المقالة أقدم الحل الخاص بي.



مقدمة صغيرة:



يعد تنسيق 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 جديدًا بنقرة واحدة.



All Articles