【问题标题】:Opencart Admin Cron JobsOpencart 管理员 Cron 作业
【发布时间】:2014-04-06 11:44:17
【问题描述】:

我了解 CRON 以及如何创建/管理它。但这个问题不同。

我想开发一个模块来删除任何超过给定时间范围的(未付款)订单。 例如:我想删除下订单后 2 天内未付款的任何未付款订单。

我想在 opencart 中使用现有模型(而不是使用新模型)。假设模块 URL 为:http://www.yourstore.com/admin/index.php?route=module/modulename/function 并且会被 CRON 调用,然后所有未付款的订单都会消失。

但主要问题是:当 CRON 想要访问那个 URL 时,它需要一个安全令牌,否则它永远不会被执行。

我的问题是:如何在没有安全令牌的情况下从 CRON 执行该模块(如果仅针对该模块)?

请帮助我,如果您有更好的想法或更干净的方法,我会非常感谢您。

【问题讨论】:

  • 为什么要在管理面板中设置?我们不能在前端设置它吗?另请注意,默认情况下,opencart 不允许在未登录的情况下访问管理页面。
  • @SankarV :不能在前端设置,因为模型在管理区域。您说的是“默认”,那么有什么办法可以覆盖它或制作一些模组?

标签: php cron url-routing opencart token


【解决方案1】:

更新:对于 Opencart 版本

对于与管理员相关的 cron 作业,请这样做。

  1. admin/index.php复制到admin/index_for_cron.php

  2. 现在,在 admin/index_for_cron.php 中,搜索这两行并将它们注释掉,它们负责登录和权限。

    // Login
    // $controller->addPreAction(new Action('common/home/login'));
    
    // Permission
    // $controller->addPreAction(new Action('common/home/permission'));
    
  3. 现在将此网址用于您的 cron 作业。

    http://www.yourstore.com/admin/index_for_cron.php?route=module/modulename/function

注意:出于安全原因,强烈建议将 index_for_cron.php 的名称更改为丑陋、不可预测的名称。

希望这会有所帮助:)

【讨论】:

  • opencart Ver 2.3 上没有这样的行
  • 如果没有从命令行执行 index_for_cron.php,我建议添加额外的安全层添加退出。这将在第一行起作用: (PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('cli only');
  • 不再是有效答案。
  • @HaydenThring,谢谢。我添加了支持的 OC 版本。还没有检查较新的版本。
【解决方案2】:

我做过类似于 IJas 的事情。在管理和目录旁边,我创建了一个名为“cli”的新文件夹。

此文件夹包含一个用于由 cli 执行的特定功能的 php 文件(通过 crontab 按设定的时间表执行脚本,或在命令行中手动执行),以及用于这些类型脚本的各种“引导程序” .引导程序本质上是在目录或管理中找到的“索引”的副本,并且包括一些检查并删除了权限检查和一些其他不必要的项目。它调用调用特定函数脚本中规定的任何控制器/动作(在下面的示例中,它调用 /admin/controller/common/cli_some_function.php 中定义的类的 index 方法)。

功能特定的脚本:

<?php
$cli_action = 'common/cli_some_function';
require_once('cli_dispatch.php');
?>

CLI“引导程序”/调度程序:

<?php

// CLI must be called by cli php
if (php_sapi_name() != 'cli') {
    syslog(LOG_ERR, "cli $cli_action call attempted by non-cli.");
    http_response_code(400);
    exit;
}

// Ensure $cli_action is set
if (!isset($cli_action)) {
    echo 'ERROR: $cli_action must be set in calling script.';
    syslog(LOG_ERR, '$cli_action must be set in calling script');
    http_response_code(400);
    exit;
}

// Handle errors by writing to log
function cli_error_handler($log_level, $log_text, $error_file, $error_line) { 
    syslog(LOG_ERR, 'CLI Error: ' . $log_text . ' in ' . $error_file . ': ' . $error_line); 
    echo 'CLI Error: ' . $log_text . ' in ' . $error_file . ': ' . $error_line; 
}
set_error_handler('cli_error_handler');

// Configuration not present in CLI (vs web)
chdir(__DIR__.'/../admin');
set_include_path(get_include_path() . PATH_SEPARATOR . realpath(dirname(__FILE__)) . '../admin/');
$_SERVER['HTTP_HOST'] = '';

// Version
define('VERSION', '1.5.1');

// Configuration (note we're using the admin config)
require_once('../admin/config.php');

// Configuration check
if (!defined('DIR_APPLICATION')) {
    echo "ERROR: cli $cli_action call missing configuration.";
    $log->write("ERROR: cli $cli_action call missing configuration.");
    http_response_code(400);
    exit;
}

// Startup
require_once(DIR_SYSTEM . 'startup.php');

// Application Classes
require_once(DIR_SYSTEM . 'library/currency.php');
require_once(DIR_SYSTEM . 'library/user.php');
require_once(DIR_SYSTEM . 'library/weight.php');
require_once(DIR_SYSTEM . 'library/length.php');

// Registry
$registry = new Registry();

// Loader
$loader = new Loader($registry);
$registry->set('load', $loader);

// Config
$config = new Config();
$registry->set('config', $config);

// Database
$db = new DB(DB_DRIVER, DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
$registry->set('db', $db);

// Settings
$query = $db->query("SELECT * FROM " . DB_PREFIX . "setting WHERE store_id = '0'");

foreach ($query->rows as $setting) {
    if (!$setting['serialized']) {
        $config->set($setting['key'], $setting['value']);
    } else {
        $config->set($setting['key'], unserialize($setting['value']));
    }
}

// Url
$url = new Url(HTTP_SERVER, HTTPS_SERVER);  
$registry->set('url', $url);

// Log 
$log = new Log($config->get('config_error_filename'));
$registry->set('log', $log);

function error_handler($errno, $errstr, $errfile, $errline) {
    global $log, $config;

    switch ($errno) {
        case E_NOTICE:
        case E_USER_NOTICE:
            $error = 'Notice';
            break;
        case E_WARNING:
        case E_USER_WARNING:
            $error = 'Warning';
            break;
        case E_ERROR:
        case E_USER_ERROR:
            $error = 'Fatal Error';
            break;
        default:
            $error = 'Unknown';
            break;
    }

    if ($config->get('config_error_display')) {
        echo "\n".'PHP ' . $error . ':  ' . $errstr . ' in ' . $errfile . ' on line ' . $errline."\n";
    }

    if ($config->get('config_error_log')) {
        $log->write('PHP ' . $error . ':  ' . $errstr . ' in ' . $errfile . ' on line ' . $errline);
    }

    return true;
}
set_error_handler('error_handler');
$request = new Request();
$registry->set('request', $request);
$response = new Response();
$response->addHeader('Content-Type: text/html; charset=utf-8');
$registry->set('response', $response); 
$cache = new Cache();
$registry->set('cache', $cache); 
$session = new Session();
$registry->set('session', $session); 
$languages = array();

$query = $db->query("SELECT * FROM " . DB_PREFIX . "language"); 
foreach ($query->rows as $result) {
    $languages[$result['code']] = $result;
}
$config->set('config_language_id', $languages[$config->get('config_admin_language')]['language_id']);
$language = new Language($languages[$config->get('config_admin_language')]['directory']);
$language->load($languages[$config->get('config_admin_language')]['filename']); 
$registry->set('language', $language);      

$document = new Document();
$registry->set('document', $document);      

$registry->set('currency', new Currency($registry));        
$registry->set('weight', new Weight($registry));
$registry->set('length', new Length($registry));
$registry->set('user', new User($registry));

$controller = new Front($registry);
$action = new Action($cli_action);
$controller->dispatch($action, new Action('error/not_found'));

// Output
$response->output();
?>

使用此方案,我可以确保不会从 Web 调用脚本,并且可以使用 cron 作业从服务器本身自动触发它(例如:0 1 0 0 0 /path/to/ php /path/to/opencart/cli/cli_some_function.php)

注意,error_handler 函数使用了一些非开箱即用的配置选项。您可以设置它们,也可以在其中填写自己的支票。

EDIT对错误处理做了一些修改

【讨论】:

  • 感谢您的解决方案。它就像一个魅力。我一直在努力将命令行参数传递给控制器​​......我当然可以在 cli_dispatch.php 中阅读它们,但我不知道如何将它们传递给我的 cli_action 控制器。你有什么建议吗?干杯
  • 您可以尝试将超全局 $_GET 或 $_POST 设置为命令行参数数组。如果您在创建请求对象之前执行此操作,则可以将其视为 get 或 post 变量 ($this->request->get['somevar'])
  • 你也可以直接从 cli_dispatch 向注册表添加东西: $registry->set('somevar', $someVal);
  • @MikeT 感谢您的解决方案。一个问题:我不能将变量传递给我的控制器。对于测试,我添加了 $registry->set('somevar', 'testvalue');在 $controller = new Front($registry); 之前。然后在控制器公共函数 test() { $this->log->write('helllllllo '.$this->request->get['somevar']); } -> 生成日志文件:未定义变量 somevar。
  • 我看到$log... 在定义之前在第 5 行被调用,这会在尝试记录错误时抛出错误。
【解决方案3】:

在 2.3.0.2 中,我发现一种非常简单的方法是将您的控制器功能路径添加到忽略的路径设置中以进行登录和权限限制。然后只需在该控制器功能中添加 url 密码或其他检查即可将其锁定。

首先在 admin/controller/startup/login.php 中将你的控制器函数路径添加到两个 $ignore 数组中,例如 'common/cron/action'

然后在 admin/controller/startup/permissions.php 你只需要控制器路径,例如'common/cron'

最后在你的 action() 函数开始时做这样的事情:

if(!isset($_GET['pass']) || $_GET['pass'] != 'secretpassword')return;

然后我将其添加到我的 cron 中:

php-cli -r 'echo file_get_contents("https://www.website.com/admin/index.php?route=common/cron/action&pass=secretpassword");'

【讨论】:

  • 我仍然为几个客户端使用 OC 2.3,这种方法是我一直做的。
【解决方案4】:

由于我多次有类似的需求,我将我的想法放入了一个名为 OCOK 的轻量级命令行工具中。

特别是 Cli 任务命令允许您通过命令行调用 Opencart 控制器,因此您可以将它们称为 cron 作业。只需像这样创建一个控制器并将其保存为admin/controller/task/example.php

class ControllerTaskExample extends Controller {
    public function index() {
        if (isset($this->is_cli) && $this->is_cli === true) {
            // work done by the controller

            if (isset($this->request->get['param1'])) {
                echo "param1 is " . $this->request->get['param1'] . "\n";
            }

            if (isset($this->request->get['param2'])) {
                echo "param2 is " . $this->request->get['param2'] . "\n";
            }

        }
    }
} 

通过命令行可以带参数调用:

ocok run task/example param1=foo param2=bar

上述命令将输出:

param1 is foo
param2 is bar

将其添加到 crontab 就像在您的 cron 文件中添加以下行一样简单:

* * * * * (cd /path/to/opencart/folder; /path/to/ocok run task/example param1=foo param2=bar)

当然需要正确设置各自的路径。

Installation 可与作曲家一起使用。所有进一步的文档都可以在文档中找到:OCOK

【讨论】:

    【解决方案5】:

    我知道这是一个非常古老的问题,但我花了很长时间试图弄清楚如何在 opencart 版本 2.x 中做同样的事情,它的工作方式不同。所以我在这里分享我的解决方案。(基于 Mike T 方法)

    1 - 创建与管理员和目录相邻的 cli 文件夹。
    2 - 在同一个文件夹中创建一个您将通过 cron 或命令行运行的文件,例如 runcron.php

    #!/usr/bin/php
    <?php
    require_once('cli_dispatch.php');
    

    3 - 在同一文件夹中创建 cli_dispatch.php 文件,该文件是 admin 文件夹中 index.php 文件的副本,并进行了一些更改(注意,在此安装中激活了 VQMOD,这可能不是您的情况)

        <?php
        // CLI must be called by cli php
        if (php_sapi_name() != 'cli') {
            syslog(LOG_ERR, "cli $cli_action call attempted by non-cli.");
            http_response_code(400);
            exit;
        }
    
        // Ensure $cli_action is set
        if (!isset($cli_action)) {
            echo 'ERROR: $cli_action must be set in calling script.';
            syslog(LOG_ERR, '$cli_action must be set in calling script');
            http_response_code(400);
            exit;
        }
    
        // Handle errors by writing to log
        function cli_error_handler($log_level, $log_text, $error_file, $error_line) {
            syslog(LOG_ERR, 'CLI Error: ' . $log_text . ' in ' . $error_file . ': ' . $error_line);
            echo 'CLI Error: ' . $log_text . ' in ' . $error_file . ': ' . $error_line;
        }
        set_error_handler('cli_error_handler');
    
    
    
        // Configuration (note we're using the admin config)
        require_once __DIR__.('/../admin/config.php');
    
    
        // Configuration not present in CLI (vs web)
        chdir(__DIR__.'/../admin');
        set_include_path(get_include_path() . PATH_SEPARATOR . realpath(dirname(__FILE__)) . '../admin/');
        $_SERVER['HTTP_HOST'] = '';
    
    
    
        if (!defined('DIR_APPLICATION')) {
            echo "ERROR: cli $cli_action call missing configuration.";
            http_response_code(400);
            exit;
        }
    
        // Version
        define('VERSION', '2.3.0.3_rc');
    
        // Configuration
        if (is_file('config.php')) {
                require_once('config.php');
        }
    
        // Install
        if (!defined('DIR_APPLICATION')) {
                header('Location: ../install/index.php');
                exit;
        }
    
        //VirtualQMOD
        require_once('../vqmod/vqmod.php');
        VQMod::bootup();
    
        // VQMODDED Startup
        require_once(VQMod::modCheck(DIR_SYSTEM . 'startup.php'));
    
        start('cli');  
    

    4 - 现在创建文件 upload/system/config/cli.php,opencart 将使用该文件从文件 upload/system/framework.php 读取新 cli bootrasp 的配置

        <?php
        // Site
        $_['site_base']         = HTTP_SERVER;
        $_['site_ssl']          = HTTPS_SERVER;
    
        // Database
        $_['db_autostart']      = true;
        $_['db_type']           = DB_DRIVER; // mpdo, mssql, mysql, mysqli or postgre
        $_['db_hostname']       = DB_HOSTNAME;
        $_['db_username']       = DB_USERNAME;
        $_['db_password']       = DB_PASSWORD;
        $_['db_database']       = DB_DATABASE;
        $_['db_port']           = DB_PORT;
    
        // Session
        //$_['session_autostart'] = true;
        // Autoload Libraries
        $_['library_autoload'] = array(
                'openbay'
        );
    
        // Actions
        $_['action_pre_action'] = array(
                'startup/startup',
                'startup/error',
                'startup/event',
                'startup/sass',
        //      'startup/login',
        //      'startup/permission'
        );
    
        // Actions
        $_['action_default'] = 'sale/croninvoices';
    
        // Action Events
        $_['action_event'] = array(
            'view/*/before' => 'event/theme'
        );  
    

    如您所见,我已经评论了所有与权限相关的 Session 和 Actions 行。
    您将不得不编辑该行

    $_['action_default'] = 'sale/yourscript';  
    

    使用控制器的路径和文件名更改“sale/yourscript”。

    在例子中,runnunig runcron.php 文件会执行索引函数

    upload/admin/controller/sale/yourscript.php file    
    

    【讨论】:

      【解决方案6】:

      在 opencart 2.1.0.2 中。 如果您在 cron 作业中需要 db,但 不需要任何 opencart 模型。 您可以创建文件系统/mycron/cron_task.php。 并将这样的代码添加到这个文件中:

      // CLI
      include_once 'config.php';
      include_once DIR_SYSTEM.'library/db/mysqli.php';
      include_once DIR_SYSTEM.'helper/general.php';
      mb_internal_encoding('UTF-8');
      if (php_sapi_name() != 'cli') { error_log('NOT CLI CALL');print 'NOT CLI CALL';http_response_code(400);exit; }
      $db = new DB\MySQLi(DB_HOSTNAME,DB_USERNAME,DB_PASSWORD,DB_DATABASE,DB_PORT);
      // END CLI
      
      // Your actual code, and you CAN use opencart DB!
      foreach ($db->query("SELECT * FROM oc_product")->rows as $row) {
          //...
      }
      

      现在您可以从您的 cron 作业中运行它:

      12 7 * * * cd /path/to/opencart/root/folder && php system/mycron/cron_task.php
      

      【讨论】:

        【解决方案7】:

        默认情况下,opencart 不允许在未登录的情况下访问管理页面。在admin/controller/common/home.phplogin() 方法中检查登录和令牌验证。

        it cant be set on frontend coz the model is in admin area. - 您可以在管理面板中为前端创建具有相同功能的新控制器和模型,并将其用于 cronjob。

        Opencart 拥有为用户设置访问权限的用户组。因此,未经许可,不会为用户加载管理页面。因此,您可能需要大量修改核心文件以在管理面板中设置 cronjob,这可能会导致严重的安全问题。

        我建议为 cronjob 提供前端控制器和模型文件。为了提高安全性,您可以在 url 中传递特定的关键参数并编写条件来验证它。

        祝你有美好的一天!!

        【讨论】:

        • 感谢您的建议..我会先尝试..非常感谢..你让我的一天更美好..
        • 无需创建单独的模型和控制器。有一个简单的方法。
        【解决方案8】:

        “我想开发一个模块来删除任何超过给定时间范围的(未付款)订单。例如:我想删除下订单后 2 天内未付款的任何未付款订单。” “我想在 opencart 中使用现有模型(而不是使用新模型)。”

        所以,我相信你知道,问题是你必须登录到管理员才能访问它的控制器和模型,但是运行时不会登录 cron 作业。

        您可以查看目录模型是否可以满足您的需求,在这种情况下没问题:

        catalog/model/checkout/order.php
        

        您可以在此处关注其他答案 - 即找到一些登录方式。

        或者您可以只编写一个运行简单 SQL 查询的独立 PHP 脚本。

        您是对的,使用系统模型通常是正确的做法,但是 OpenCart 非常简单,它应该是一个非常简单的查询(只需几行),它可以满足您的需要,所以这也是在这种情况下,我认为这是一个可以接受的选择。

        【讨论】:

          【解决方案9】:
          include_once($_SERVER['DOCUMENT_ROOT'].'/admin/model/module/ModelYourModel.php');
          $mym = new ModelYourModel($this->registry);
          $mym->yourFunction();
          

          对于 2.1 版,可能更高。使用目录文件夹中 admin 文件夹中的模型。

          【讨论】:

            猜你喜欢
            • 2018-06-20
            • 1970-01-01
            • 2015-09-27
            • 1970-01-01
            • 1970-01-01
            • 2014-11-04
            • 2016-11-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多