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(' ', '', $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
这个算是框架问题,唉,只能代码逻辑避免出现上面出现上面的代码错误,减少或者不写入错误写入日志
@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