关于回写脚本框架与自动加载autoload的一点总结

今天遇到一个奇怪的问题,在命令行下运行一个脚本,使用 php cron_test.php 可以正常运行;但是,回到上一级目录 cd ../ ,然后

使用 php async_script/cron_test.php 却不能完整运行,遇到加载其他类的地方就停止了,但是也不报错,try...catch...也没用信

息,各种调试了好久,后来猜测这个类是不是没有被加载进来呢?但是又一想,如果没有加载到,也会报错啊,这啥提示也没有。。。

情况如下:

目录结构如下:

dc_leba_new\
    async_script\
        Core\
        Lib\
            Common.php
        Logic\
            Factory.php
        Logs\
        vendor\
        ...
        cron_test.php
        ...


cron_test.php的代码
    require('Lib/Common.php');

    use Logic\Factory;

    class cron_test
    {
        public $logTag = 'cron_test';
        public $log;

        public function main(){
            echo 1 .PHP_EOL;

            $this->log = Factory::getLogger();

            echo 2 .PHP_EOL;

            $this->log->info($this->logTag, 'cron start');
        }
    }

    $obj = new cron_test();
    $obj->main();

执行  php async_script/cron_test.php 只输出 1 然后就退出了。

执行  cd async_script
    php cron_test.php 输出 1 和 2,完整的执行了。

考虑到可能是类加载出了问题,于是查看 Lib/Common.php

    <?php

    use \Logic\Factory;

    $root =  dirname(__DIR__);

    require $root . '/vendor/autoload.php';

    set_exception_handler('output_exception');
    set_error_handler('output_error');
    register_shutdown_function('output_shutdown');
    spl_autoload_register('my_autoload');

    date_default_timezone_set('Asia/Shanghai');
    ini_set('error_reporting', 'E_ALL');

    function output_exception($e){
        $logs = Factory::getLogger();
        $msg = $e->getFile() . ' | lime ' . $e->getLine() . ' | ' . $e->getMessage();
        $logs->error('sys_exception', $msg);
    }
    function output_error($errno, $errstr, $errfile, $errline){
        $logs = Factory::getLogger();
        $msg = $errfile . ' | lime ' . $errline . ' | ' . $errstr . ' | errno ' . $errno;
        $logs->error('sys_error', $msg);
    }
    function output_shutdown(){
        $logs = Factory::getLogger();
        $data = error_get_last();
        if(isset($data['message']) && !is_null($data['message'])) {
            $msg = $data['file'] . ' | line ' . $data['line'] . ' | ' . $data['message'];
            $logs->fatal('sys_shutdown', $msg);
        }
    }

    function my_autoload($class_name){
        $full = str_replace('\\', '/', $class_name);
        require_once $full . '.php';
    }

试着在 my_autoload 函数中打印 $full, 输出的是 Logic/Factory ,于是发现了问题所在

当我执行 php async_script/cron_test.php 时,

php解释器的当前目录是在 dc_leba_new ,因此我的 my_autoload 会加载  dc_leba_new/Logic/Factory 显示是没有的。

那么第二题又来了,既然找不到这个文件,那php解释器为什么不报错呢?

仔细分析Connme,php文件,虽然定义了各种捕获错误与异常的方法,但是别忘了,他们只能捕获引入的文件中的错误,当前文件的错误是不捕获的,

也不提示,这一点我们研究那些开源框架就知道了,知道了问题后就来修改:

Common.php文件修改后如下:

    <?php

    use \Logic\Factory;

    define('ROOT', dirname(__DIR__));

    require ROOT . '/vendor/autoload.php';

    set_exception_handler('output_exception');
    set_error_handler('output_error');
    register_shutdown_function('output_shutdown');
    spl_autoload_register('my_autoload');

    date_default_timezone_set('Asia/Shanghai');
    ini_set('error_reporting', 'E_ALL');

    function output_exception($e){
        $logs = Factory::getLogger();
        $msg = $e->getFile() . ' | lime ' . $e->getLine() . ' | ' . $e->getMessage();
        $logs->error('sys_exception', $msg);
    }
    function output_error($errno, $errstr, $errfile, $errline){
        $logs = Factory::getLogger();
        $msg = $errfile . ' | lime ' . $errline . ' | ' . $errstr . ' | errno ' . $errno;
        $logs->error('sys_error', $msg);
    }
    function output_shutdown(){
        $logs = Factory::getLogger();
        $data = error_get_last();
        if(isset($data['message']) && !is_null($data['message'])) {
            $msg = $data['file'] . ' | line ' . $data['line'] . ' | ' . $data['message'];
            $logs->fatal('sys_shutdown', $msg);
        }
    }

    function my_autoload($class_name){
        $full = str_replace('\\', '/', $class_name);
        $file = ROOT . '/' . $full . '.php';
        if(file_exists($file)){
            require_once $file;
        }else{
            // 考虑到一些没有定义命名空间的类,和系统内置的类
            $ext = $full . '.php';
            require_once $ext;
        }

    }

主要改动:

1、增加了ROOT常量
    __DIR__ 的值就是Common.php所在的文件夹 即 Lib,再使用 dirname() 函数,得到 async_script,就是项目根目录了。

2、将 my_autoload() 函数改为 绝对路径加载,这样不管在哪个目录下执行都不会有问题。

3、待改进的是:
    1、将自定义异常与错误处理单独放一个文件
        set_exception_handler('output_exception');
        set_error_handler('output_error');
        register_shutdown_function('output_shutdown');
        spl_autoload_register('my_autoload');

    2、将捕获处理方法放到另一个文件中,因为不能排除这里也会有错
        function output_exception($e){
            $logs = Factory::getLogger();
            $msg = $e->getFile() . ' | lime ' . $e->getLine() . ' | ' . $e->getMessage();
            $logs->error('sys_exception', $msg);
        }
        function output_error($errno, $errstr, $errfile, $errline){
            $logs = Factory::getLogger();
            $msg = $errfile . ' | lime ' . $errline . ' | ' . $errstr . ' | errno ' . $errno;
            $logs->error('sys_error', $msg);
        }
        function output_shutdown(){
            $logs = Factory::getLogger();
            $data = error_get_last();
            if(isset($data['message']) && !is_null($data['message'])) {
                $msg = $data['file'] . ' | line ' . $data['line'] . ' | ' . $data['message'];
                $logs->fatal('sys_shutdown', $msg);
            }
        }

    3、将 my_autoload() 方法单独放到一个文件,这是最容易出错的。


于是这引发了我对 php中,当前路径和相对路径的思考,参看另外一篇文章。

猜你喜欢

转载自blog.csdn.net/raoxiaoya/article/details/92371941