zoukankan      html  css  js  c++  java
  • Laravel开发采坑系列问题

    2017年12月22日17:40:03 不定时更新

    版本5.4.X

    一下是可能会遇到的坑

    1,必须的写路由转发才能访问控制器,当然你可以自动路由访问,但是需要些匹配规则,其实还是转发了

    好多人讨论过自动路由的缺点,但是中小项目用不上,而且暴露在外的接口,现在大多数都是有路由转发,完全可以通过分组来兼容多种开发习惯和需求,并不是自动路由就狗屁不是

    2,Laravel 做计划任务的时候坑真的好多,比如不能直接跨控制器访问,web的是web的路由,console是它自己的,所以你的功能和逻辑代码必须在Repository或者service里面,不然你懂的,做好逻辑代码分离

    官方文档只有用过的才能看得懂,我很无奈

    完整流程

    appConsoleCommands下建立你的任务文件 

    SpiderTask.php
    <?php
    
    namespace AppConsoleCommands;
    
    use IlluminateConsoleCommand;
    //use IlluminateSupportFacadesRedis;
    use AppRepositoriesSpiderRepository;//具体逻辑代码
    
    class SpiderTask extends Command {
    
        protected $taskserver;
    
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'SpiderTask';
    
        /**
         * The console command description.
         *
         * @var string
         */
        protected $description = 'spider every one hour crawl data';
        protected $spider;
    
        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct() {
    
            parent::__construct();
            $this->spider = new SpiderRepository();
        }
    
        /**
         * Execute the console command.
         *
         * @return mixed
         */
        public function handle() {
            $this->spider->do_all();//具体执行地方
        }
    
    }

    然后注册到Kernel.php

    <?php
    
    namespace AppConsole;
    
    use IlluminateConsoleSchedulingSchedule;
    use IlluminateFoundationConsoleKernel as ConsoleKernel;
    use AppRepositoriesSpiderRepository;//我的具体逻辑代码地方
    
    class Kernel extends ConsoleKernel {
    
        /**
         * The Artisan commands provided by your application.
         *
         * @var array
         */
        protected $commands = [
            //
            'AppConsoleCommandsSpiderTask', //必须
        ];
    
        /**
         * Define the application's command schedule.
         *
         * @param  IlluminateConsoleSchedulingSchedule  $schedule
         * @return void
         */
        protected function schedule(Schedule $schedule) {
    //        $schedule->call(function () {
    //            $Spider = new SpiderRepository();
    //            $Spider->do_all();
    //        })->daily();
            $schedule->command('SpiderTask')->daily();
    //两种方法都可以,建议第二种,逻辑更清晰 }
    /** * Register the Closure based commands for the application. * * @return void */ protected function commands() { require base_path('routes/console.php'); } }

    注意:

    你要测试你的代码逻辑有没有错误

    最好在Linux下测试,因为windows好多问题

    在代码根目录,如果命令没有到全局,使用完整路径

    php artisan schedule:run

    everyMinute才会实时运行,可以看到报错  

    $schedule->command('SpiderTask')->everyMinute();

    http://laravelacademy.org/post/6931.html 官方文档

    <?php
    
    namespace AppRepositories;
    
    use AppModelsSpider;
    //use phpspidercorephpspider;
    use phpspidercore
    equests;
    use phpspidercoreselector;
    
    class SpiderRepository {
    
        use BaseRepository;
    
        protected $model;
    
        /**
         * ActivityRepository constructor.
         * @param Activity $activity
         */
        public function __construct() {
            $this->model = new Spider();
        }
    
        public function do_all() {
            $this->cjysjs();
            $this->shysjs();
            $this->nchn();
            $this->ltbj();
        }
    
        //长江有色金属
        public function cjysjs() {
            $html = requests::get('http://www.ccmn.cn/');
            $data = selector::select($html, "#40288092327140f601327141c0560001", "css");
            $data1 = selector::select($data, "tr", "css");
            array_shift($data1);
    
            $array = array();
            if (!empty($data1) && is_array($data1)) {
                foreach ($data1 as $k => &$v) {
                    $data2 = selector::select($v, "td", "css");
                    foreach ($data2 as $kk => &$vv) {
                        $vv = str_replace('&#13;', '', $vv);
                        $vv = str_replace(array("
    ", "
    ", "
    "), "", $vv);
                        $vv = trim($vv);
                    }
                    $data2['3'] = selector::select($data2['3'], "font", "css");
                    unset($data2['6']);
                    $array[] = $data2;
                }
    
                if (empty($array)) {
                    $info = date("Y-m-d H:i:s", time()) . ':长江有色金属抓取失败!';
                    Log::info($info);
                }
                $name = 'cjysjs';
                $_data = [];
                if (!empty($array) && is_array($array)) {
                    $_data['value'] = json_encode($array);
                    $_data['crawl_time'] = time();
    
                    $count = $this->getData($name);
                    if (empty($count)) {
                        //增加
                        $_data['name'] = $name;
                        $result = $this->saveData(null, $_data);
                    } else {
                        //更新
                        $_data['name'] = $name;
                        $result = $this->saveData($name, $_data);
                    }
                }
            }
        }
    
     public function saveData($name = null, $data = null) {
            return $this->model->updateOrCreate(['name' => $name], $data);
        }
    
        public function getData($name) {
            return $this->model->where('name', $name)->count();
        }
    
    }

    添加计划任务

    * * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
    /dev/null 2>&1 必须,其实为了把错误信息和输出输出到/dev/null 文件
    注意php是否是全局,不是就全路径,artisan网站根目录

    获取错误
    Laravel 调度器为处理调度任务输出提供了多个方便的方法。首先,使用sendOutputTo 方法,你可以发送输出到文件以便稍后检查:
    
    $schedule->command('emails:send')
         ->daily()
         ->sendOutputTo($filePath);
    如果你想要追加输出到给定文件,可以使用 appendOutputTo 方法:
    
    $schedule->command('emails:send')
         ->daily()
         ->appendOutputTo($filePath);
    使用 emailOutputTo 方法,你可以将输出发送到电子邮件。使用电子邮件发送任务输出之前,需要配置 Laravel 的电子邮件服务:
    
    $schedule->command('foo')
         ->daily()
         ->sendOutputTo($filePath)
         ->emailOutputTo('foo@example.com');

    3,laravel增加第三类或者库文件

    lavavel是封装的比较好的框架,那么composer,要么遵循他的规范,我在使用tcpdf在laravel5.4的时候,发现一个问题

    laravel的某种组件要php7以上版本,服务器上又只有5.6我又不想麻烦,想直接引入tcpdf,发现完全不行,la回去直接通过空间路径去引入文件读取,

    这样一来,就不能直接引入,解决方案不难,也不用升级php版本,

    composer.json 
    
    "autoload": {
            "classmap": [
                "database",
                "app/Libarary/tcpdf"
            ],
            "psr-4": {
                "App\": "app/"
            },
            "files": [
                "app/Tools.php",
                "app/WeiXin.php"
            ]
        },

    我引入tcpdf在app下面的/Libarary/tcpdf

    然后

    composer dumpautoload  

    注意,如果你本地composer比较麻烦,你可以在虚拟机上执行,在吧代码拉回来,因为windows上实在难用

    代码使用比较简单

    use TCPDF;
    
    public function index() {
    $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
    }

    就OK了

    4,在某些特殊环境下模型的 create方法,会把很大的数字 2018011900012 转换成 -622729113的负数,数据库存的string varchar,底层原因未知

    解决方法,如果数据库里是varchar ,自己手动强制转换一下,如果是 (string)$order_key; 如果是int,你也可能会出现这个问题,也需要转换一下(int)

    5,

    location / {
                try_files $uri $uri/ /index.php?$query_string;
                index  index.html index.htm index.php;
                #autoindex  on;
            }

     正常在NGINX的配置文件都需要加上这一段laravel才可以正常运行,但是直接从网上的找了一段是这样的

    location / {
                try_files /$uri /$uri/ /index.php?$query_string;
                index  index.html index.htm index.php;
                #autoindex  on;
            }

    坑爹导致 所有get参数都会现成

    比如 index.php?type=ee

    $this->request->all();

    array{

     [ ype] => ee

    }

    还以为是laravel需要什么特殊处理,结果是配置文件出错

     5,laravel意外bug

    #0 [internal function]: IlluminateFoundationBootstrapHandleExceptions->handleError(2, 'array_shift() e...', '/data/wwwroot/z...', 37, Array)
    #1 /data/wwwroot/zs_web/app/Repositories/SpiderRepository.php(37): array_shift(false)
    #2 /data/wwwroot/zs_web/app/Repositories/SpiderRepository.php(26): AppRepositoriesSpiderRepository->cjysjs()
    #3 /data/wwwroot/zs_web/app/Console/Commands/SpiderTask.php(45): AppRepositoriesSpiderRepository->do_all()
    #4 [internal function]: AppConsoleCommandsSpiderTask->handle()
    #5 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(30): call_user_func_array(Array, Array)
    #6 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(87): IlluminateContainerBoundMethod::IlluminateContainer{closure}()
    #7 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): IlluminateContainerBoundMethod::callBoundMethod(Object(IlluminateFoundationApplication), Array, Object(Closure))
    #8 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/Container.php(539): IlluminateContainerBoundMethod::call(Object(IlluminateFoundationApplication), Array, Array, NULL)
    #9 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Console/Command.php(182): IlluminateContainerContainer->call(Array)
    #10 /data/wwwroot/zs_web/vendor/symfony/console/Command/Command.php(274): IlluminateConsoleCommand->execute(Object(SymfonyComponentConsoleInputArgvInput), Object(IlluminateConsoleOutputStyle))
    #11 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Console/Command.php(168): SymfonyComponentConsoleCommandCommand->run(Object(SymfonyComponentConsoleInputArgvInput), Object(IlluminateConsoleOutputStyle))
    #12 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(952): IlluminateConsoleCommand->run(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
    #13 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(231): SymfonyComponentConsoleApplication->doRunCommand(Object(AppConsoleCommandsSpiderTask), Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
    #14 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(132): SymfonyComponentConsoleApplication->doRun(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
    #15 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(122): SymfonyComponentConsoleApplication->run(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
    #16 /data/wwwroot/zs_web/artisan(36): IlluminateFoundationConsoleKernel->handle(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
    #17 {main} 
    array_shift(false)

    导致这样的一个bug

     

    代码会招致权限问题,我的天,经过分析是write的时候

    protected function write(array $record)
        {
            if (!is_resource($this->stream)) {
                if (null === $this->url || '' === $this->url) {
                    throw new LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
                }
                $this->createDir();
                $this->errorMessage = null;
                set_error_handler(array($this, 'customErrorHandler'));
                $this->stream = fopen($this->url, 'a');
                if ($this->filePermission !== null) {
                    @chmod($this->url, $this->filePermission);
                }
                restore_error_handler();
                if (!is_resource($this->stream)) {
                    $this->stream = null;
                    throw new UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url));
                }
            }
    @chmod($this->url, $this->filePermission); 偶尔出现权限问题,不是可以重现bug
    这个算是框架问题,唉,只能代码逻辑避免出现上面出现上面的代码错误,减少或者不写入错误写入日志
     核心原因是跑计划任务的用户是当前登陆用户,而php用户是www,偶尔会出现问题
     
    @chmod($this->url, 0777); 就OK了

    不改代码的方案是 crontab -e -u www
    www 用户是php的用户,吧
    * * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1 写在里面就O



    6,laravel 5.6 mews/captcha 使用小坑
    注意版本
    1. 安装
    1). 使用 composer 安装:
    
    composer require mews/captcha
    
    2). 修改 config/app 文件,添加 ServiceProvider:
    
    在 providers 数组内追加如下内容
    
    'providers' => [
        ...
        MewsCaptchaCaptchaServiceProvider::class,
    ],
    
    在 aliases 数组内追加如下内容
    
    'aliases' => [
        ...
        'Captcha' => MewsCaptchaFacadesCaptcha::class,
    ],
    
    3). 运行 php artisan vendor:publish 生成配置文件 config/captcha.php

    如果你这么操作了,新建一个CaptchaController去创建一个验证码,但是5.6不是这样的 

    你直接访问 http://127.0.0.1/captcha  就会去读取你生产的配置文件,不需要做任何操作

    注意:你如果按照以前的比如5.4

    public function mews() {
    
           return Captcha::create('default');
        }


    以前就是这样操作,访问一个新的路由,如果你还是这样做就无法按照
    config/captcha.php的配置生产你的需要的格式的验证码

    7,5.6版本一些常见报错和原因
    Symfony  Component  HttpKernel  Exception  MethodNotAllowedHttpException
    405 Method Not Allowed

    一般是因为路由 get post请求方式错误
    419 unknown status
    一般是发送的请求有 csrf-token校验 ,但是你没有发送csrf-token这个参数,注意查看http头是否带有csrf-token


  • 相关阅读:
    java入门-使用idea创建web项目
    java入门-gitlab
    linux基础:source和sh的区别
    github基本使用
    docker-compose
    k8s学习笔记之六:flannel网络配置
    计算机网络
    python自学之路--python面试题
    ASP.NET前后端分离框架(转载)
    ASP.NET Core初步使用Quartz.NET(转载)
  • 原文地址:https://www.cnblogs.com/zx-admin/p/8086792.html
Copyright © 2011-2022 走看看