Laravel开发采坑系列问题

时间:2023-02-10 07:46:51

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

版本5.4.X

一下是可能会遇到的坑

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

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

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

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

完整流程

app\Console\Commands下建立你的任务文件

SpiderTask.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
//use Illuminate\Support\Facades\Redis;
use App\Repositories\SpiderRepository;//具体逻辑代码 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 App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\Repositories\SpiderRepository;//我的具体逻辑代码地方 class Kernel extends ConsoleKernel { /**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
'App\Console\Commands\SpiderTask', //必须
]; /**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $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 App\Repositories;

use App\Models\Spider;
//use phpspider\core\phpspider;
use phpspider\core\requests;
use phpspider\core\selector; 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("\r\n", "\r", "\n"), "", $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{

[\type] => ee

}

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

5,laravel意外bug

#0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->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): App\Repositories\SpiderRepository->cjysjs()
#3 /data/wwwroot/zs_web/app/Console/Commands/SpiderTask.php(45): App\Repositories\SpiderRepository->do_all()
#4 [internal function]: App\Console\Commands\SpiderTask->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): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#7 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#8 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/Container.php(539): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#9 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Console/Command.php(182): Illuminate\Container\Container->call(Array)
#10 /data/wwwroot/zs_web/vendor/symfony/console/Command/Command.php(274): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#11 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Console/Command.php(168): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#12 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(952): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#13 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(231): Symfony\Component\Console\Application->doRunCommand(Object(App\Console\Commands\SpiderTask), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#14 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(132): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#15 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(122): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#16 /data/wwwroot/zs_web/artisan(36): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#17 {main}
array_shift(false)

导致这样的一个bug

Laravel开发采坑系列问题

代码会招致权限问题,我的天,经过分析是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' => [
...
Mews\Captcha\CaptchaServiceProvider::class,
], 在 aliases 数组内追加如下内容 'aliases' => [
...
'Captcha' => Mews\Captcha\Facades\Captcha::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