用于Opencart在线商店的OCmod文件生成器

在为流行的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>之间的位置)。这必须由程序员来完成。通常,他们试图使其尽可能通用,以涵盖源的更多潜在版本,同时使其确切地在需要的地方进行更改。这显然是手工制作的。其他一切都是自动化的。



一个小题外话:搜索发生在整个字符串中,并且只能在插入之前,之后或代替插入,但不能在内部插入(在Opencart的经典OCMOD软件包中)。这是我个人无法理解的限制。而且,我不明白为什么不可能设置几个<search>标签来找到所需的插入点,而插入点将被一致地处理-因为搜索会更加灵活。例如,如果在PHP代码中,则找到函数的名称,然后在函数中找到正确的位置,或者由程序员自行决定以其他方式确定。但是我没有找到这个,如果我弄错了,请更正它。



现在最重要的是:您可以自动化创建ocmod文件的过程,而您只需要遵循所需的方案即可。首先,在源文件中,我们需要以某种方式指出更改的位置-既是为了顺序,又是为了使ocmod生成器可寻址地知道所有内容。假设我们的名字叫Pyotr Nikolaevich Ivanov(巧合是随机的)。让我们将所有更改包含在<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转义,我们只需编写搜索字符串和代码。



另一个示例,已经针对我们正在修改的树枝文件:



            {# <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的命令管理器。我认为Linux上的谁会重写而不会出现问题。



简介:我认为,这种方法比每次手动编辑ocmod都容易得多。添加代码时,我们立即在该代码段中设置了搜索标签,然后仅关注工作。我们不再关心xml文件的结构,并且当场对我们的修改进行了任何更正,立即检查其工作,然后一键生成一个新的ocmod文件。



All Articles