PHP中的框架众多,我本身就接触了好几个。大学那会啥也不懂啥也不会,拿了一个ThinkPHP学了。也许有好多人吐槽TP,可是我的感受不能说哪一个框架好,哪一个框架很差,再很差的框架你能把源码读上一遍,框架的设计思想理解了也能学到好多东西。何况有好多东西本身还不理解,因此认真学习一个框架这仍是能够学很多东西的。php
仍是先说说Laravel吧,如今已经到5.2了。就我本身来讲以前没有接触过laravel,可是学习过laravel以后感受这个框架确实不错,而且老外用的不亦乐乎。他的开发社区还能够,文档比较齐全,可是官网文档不咋地,从上面读不出多少东西(本身感受),好多东西还得阅读源码,对于我这种英语很差的人还更喜欢中文文档(之后得改)。Laravel是使用Composer(https://getcomposer.org)来管理依赖,确实比较方便,可是由于镜像被墙的缘由在访问或者更新的时候比较慢(几乎失败),这里有解决办法:http://pkg.phpcomposer.com/#tip1。html
最近由于工做须要项目要重构(重构缘由不用多说,你们懂得),须要迁移到新的框架上。Laravel是一个不错的框架,强大的路由,便捷的配置,高可用的模块依赖,确实为开发省了很多力气。考虑到咱们这个项目主要是接口部分,对性能有必定的要求(可是不是苛刻),而且路由不能改,要兼容老的逻辑,因此Laravel是首选,可是有一个问题就是咱们是写接口,那么要依赖的东西就少不少,好比view层几乎用不到,还有就是测试模块,上传模块(有图床),本地化模块文件系统等也用不到,因此使用Laravel仍是比较浪费的,说白了他比较重。因此咱们就考虑了基于laravel的一个框架Lumen,相比Laravel这个全栈框架而言Lumen精简了很多,而且Lumen是面向Api的,因此最后就选择Lumen了。laravel
laravel也不是全能的有优势也有缺点,好比他的依赖太多,能够看一下安装好的laravel框架默认的依赖源包就有30M左右,确实有点大。直到今天在使用过程当中发现Lumen也是有点力不从心,随着业务逻辑愈来愈复杂,访问速度各方面也下来了,咱们有时候考虑 slim 等更轻量级的,其实吧新浪这边很多人是鸟哥粉,很多人推崇yaf,yaf确实牛逼,实践证实快的不止一点两点,估计之后还得迁移到yaf。这段时间PHP7不是出了吗,可是测试结果代表Bug很多,把接口迁移到7上应该有很多的性能提高,听说是提升100%,还没敢尝试,等稳定了再说吧!数据库
说了很多废话,下面我就介绍一下Laravel中的日志与上传,慢慢来,这篇文章先写一部分,由于Laravel东西比较多,其余的我会慢慢写出来。我就说说本身在使用过程当中遇到的问题,遇到的坑,帮助你们学习。json
1、日志数组
一、说明:php框架
日志重要性不言而喻,咱们这边的日志是人工推荐,兴趣爱好,推荐位的依赖。作推荐的同窗比写接口的还要多,日志出了问题,推荐就会不许确甚至没法推荐,可见日志的重要性。服务器
Laravel框架初始化好了之后错误和异常处理已经默认配置好了,他的日志是基于一款很好用的日志管理工具Monolog,app
首先说一下Monolog,是php下比较全又容易扩展的记录日志组件。其中Symfony 、 CakePHP等知名php框架都内置了Monolog,有兴趣的能够看一下。每一个Logger实例都有一个通道和日志处理器栈。每当你添加一条日志记录,它会被发送到日志处理器栈。 你能够建立不少Logger,
每一个Logger定义一个通道(db,请求,路由),每一个Logger有不少日志处理器。这些通道会过滤日志。每一个日志处理器都有一个Formatter(内置的日志显示格式处理器)。你还能够设定日志级别。(官网解释)composer
日志配置:Laravel目前支持四种日志处理器,
1 Single(将日志记录到单个文件中。该日志处理器对应,对应StreamHandler), 2 3 Daily (以日期为单位将进行日志记录对应RotatingFileHandler) 4 5 Syslog(将日志记录到Syslog中。对应SysLogHandler) 6 7 Errorlog(将日志记录到PHP的error_log中。对应ErrorLogHandler)
明白了日志的处理方式咱们就能够设置本身须要的方式,在 config/app.php中的对应项设置(默认的):
1 'log' => 'single',
二、使用Log记录日志
Laravel提供了Log方法记录日志,Log
实际上使用的 Illuminate\Log\Writer
,应为在其中 Writer
的构造函数中注入了Monolog\Logger
。生成的日志文件存放在storage/logs
目录下。
以下:
1 Log::emergency($error); //紧急,如系统挂掉 2 Log::alert($error); //须要当即采起行动,如数据库异常等 3 Log::critical($error); //严重问题,如异常 4 Log::error($error); //运行时错误,不须要当即处理但须要被记录和监控 5 Log::warning($error); //警告但不是错误,好比使用了被废弃的API 6 Log::notice($error); //普通但值得注意的事件 7 Log::info($error); //感兴趣的事件,好比登陆、退出 8 Log::debug($error); //详细的调试信息
三、按照本身的需求记录日志
Laravel中若是按照原来的配置貌似不能按照本身的需求记录日志,我就按照本身的需求写了一个,供你们参考,固然你能够跳过他提供的日志处理方法Log,在容器中把 Monolog对象写入容器,能够写成单例的形式,这样在加载的时候只实例化一次,而后按照monolog来配置本身想要的记录日志的方法。
1 class Save_log 2 { 3 //存放每一个级别实例 4 private static $obj_log = []; 5 6 //日志类型映射 7 private static $classify_arr = ['default', 'debug_log','error_log']; 8 9 /** 10 * 单利初始化以及调取对象 11 * @param $classify 日志的的频道,对应不一样的目录 12 * @param $max_num 日志记录的最大数量 13 */ 14 public static function get_log_instance($classify = 'default', $max_num = 0) 15 { 16 if(empty(self::$obj_log[$classify])) { 17 self::$obj_log[$classify] = new Writer(new Logger($classify)); 18 self::$obj_log[$classify]->useDailyFiles(self::get_path($classify), $max_num); 19 } 20 return self::$obj_log[$classify]; 21 } 22 23 /** 24 * 映射对应的目录 25 * @param $classify 日志的不一样的频道 26 */ 27 private static function get_path($classify) 28 { 29 $root_path = public_path(); 30 $path = $root_path . '/../../logs/'; //能够是本身的任意路径 31 $log_arr = self::$classify_arr; 32 if(!empty($log_arr) && !empty($classify)) { 33 if(in_array($classify, $log_arr)) { 34 return $path . $classify. '/' . $classify . '.log'; 35 } 36 } 37 return $path . 'default/default.log'; 38 } 39 40 /** 41 * 映射对应的目录 42 * @param $func 调用的方法 43 * @param $arguments 参数,包括数据和日志等级 44 */ 45 public static function __callStatic($func, $arguments) 46 { 47 $get_obj = self::get_log_instance($func); 48 if(empty($get_obj)) { 49 log::error('Save Log Error!'); 50 } 51 if(empty($arguments) || !is_array($arguments) || !isset($arguments[0])) { 52 $get_obj->info('No Data Save!'); 53 } else if(!isset($arguments[1])) { 54 $get_obj->info($arguments[0]); 55 } else { 56 $get_obj->{$arguments[1]}($arguments[0]); 57 } 58 } 59 }
使用的时候能够指定,以下:
1 Save_log::error_log($info, 'error'); 2 Save_log::debug_log($info);
日志内容以下:
2、上传文件。
Laravel中的上传文件是基于Flysystem提供的文件系统来实现上传,删除,移动。他支持多种驱动,还有一个值得看的云存储,在SAE上须要用到。
文件系统配置位于Config/filesystems.php,我使用的试本地驱动。Laravel中的上传目录有两个:public和Storage两个,有人说这两个同样,实际上是有区别的,应该说是各有好处,若是放在public中,服务器能够直接控制访问,方便效率高,放在Storage中能够加上用户控制好比权限等。
上传须要的函数以下:
判断是否进行了上传,是否存在文件:
1 $request->hasFile('file')
判断上传是否出错:
1 $file = $request->file('file'); 2 //判断文件上传过程当中是否出错 3 if(!$file->isValid()) { 4 exit('文件上传出错!'); 5 }
肯定上传:
1 $bytes = Storage::put( 2 $savePath, 3 file_get_contents($file->getRealPath()) 4 );
你也可使用:
$path = $file -> move('storage/uploads');
生成缩略图
Laravel木有提供函数生成缩略图,可是咱们能够借助强大的Composer来引入图片处理库 Integration/Image
在项目根目录中的composer.json中的require中添加:"intervention/image": "dev-master",以下图:
而后在config/app.php中providers数组中添加:
1 Intervention\Image\ImageServiceProvider::class
在aliases数组中添加别名:
1 'Image' => Intervention\Image\Facades\Image::class,
这样就可使用了,在类文件中添加:
1 use Image;
下面是添加水印而且生成缩略图:
$Image->text('@ u/'. $user_id, $news_width - 40 - $length * 10, $news_height - 24, function($font) { $font->file('public/foos.ttf'); $font->size(14); $font->color('#ffffff'); });
最后附上整个源码,其中生成缩略图部分能够抽象出来,由于有好几个地方都须要用到,而且水印还有看图片大小等等。
1 /** 2 * 上传文件 3 * @param Object Request 4 * @return Json result 5 */ 6 public function upload_file(Request $request) 7 { 8 $user_id = $request->get('user_id'); 9 $width = $request->get('width'); 10 $height = $request->get('height'); 11 $upload_type = $request->get('upload_type'); 12 $watermark = $request->get('watermark'); 13 14 //参数检查 15 if(empty($user_id)) { 16 return response()->json(['code' => 1001, 'msg' => '参数错误']); 17 } 18 19 //获得上传文件名 20 if(!empty($_FILES)) { 21 $key_arr = array_keys($_FILES); 22 $file_key = $key_arr[0]; 23 } 24 25 $file_key = !isset($file_key) || empty($file_key) ? 'fileselect' : $file_key; 26 27 if(!$request->hasFile($file_key)) { 28 return response()->json(['code' => 1002, 'msg' => '上传文件为空']); 29 } 30 31 $upload_files = $request->file(); 32 if(empty($upload_files) || !is_array($upload_files)) { 33 return response()->json(['code' => 1003, 'msg' => '上传失败']); 34 } 35 36 //兼容单文件上传 37 if(Utils::arrayLevel($upload_files) < 2) { 38 $files[$file_key][0] = $upload_files[$file_key]; 39 } else { 40 $files = $upload_files; 41 } 42 43 if($upload_type == 'userphoto' && count($files[$file_key]) > 1) { 44 return response()->json(['code' => 1004, 'msg' => '头像只能上传一张']); 45 } 46 47 if(count($files[$file_key]) > MAX_UPLOAD_FILE) { 48 return response()->json(['code' => 1005, 'msg' => '大于最大上传数限制']); 49 } 50 51 //过滤大于MAX_FILE_SIZE的状况 52 foreach ($files[$file_key] as $key => $file) { 53 if($file-> getClientSize() > MAX_FILE_SIZE * 1024 * 1024) { 54 return response()->json(['code' => 1006, 'msg' => '文件大小不能超过']); 55 } 56 } 57 58 $file_info = []; 59 $length = strlen($user_id . ''); 60 //兼容批量上传 61 foreach ($files[$file_key] as $key => $file) { 62 if(!$file->isValid()) { 63 return response()->json(['code' => 1007, 'msg' => '上传出错']); 64 } 65 66 if($upload_type == 'userpic') { 67 $file_dir = 'userpic'; 68 } else { 69 $type = $file->getMimeType(); 70 if(empty($type) && !is_array($type)) { 71 return response()->json(['code' => 1008, 'msg' => '获得文件类型出错']); 72 } 73 74 //映射文件类型 75 $type_arr = explode("/", $type); 76 switch($type_arr[0]){ 77 case "image" : $file_dir = "image"; break; 78 case "video" : $file_dir = "video"; break; 79 case "audio" : $file_dir = "voice"; break; 80 case "text" : $file_dir = "doc"; break; 81 case "application": $file_dir = "doc"; break; 82 default : $file_dir = "other"; break; 83 } 84 } 85 86 //文件后缀 87 $postfix = $file->getClientOriginalExtension(); 88 $save_dir = UPLOAD_FILE_PATH; 89 $file_date = date('Ym'); 90 $file_name = $file_dir . '_' . $file_date . '_' . rand(111111, 999999) . $user_id; 91 $save_name = $file_name . '.' . $postfix; 92 $save_path = $file_dir . '/' . $file_date . '/' . $save_name; 93 Storage::put( 94 $save_path, 95 file_get_contents($file->getRealPath()) 96 ); 97 if(!Storage::exists($save_path)) { 98 return response()->json(['code' => 1009, 'msg' => '保存文件失败']); 99 } 100 101 //生成缩略图 102 if($file_dir == 'image' && (!empty($width) || !empty($height))) { 103 $Image = Image::make($save_dir . $save_path); 104 $img_width = $Image->width(); 105 $img_height = $Image->height(); 106 107 //若是有一个为空,则与另外一个相等; 108 if(empty($width)) { 109 //传入的高度若是比实际高度大,就取实际高度 110 $height = $img_height < $height ? $img_height : $height; 111 $width = $height; 112 } else if(empty($height)) { 113 $width = $img_width < $width ? $img_width : $width; 114 $height = $width; 115 } else { 116 $height = $img_height < $height ? $img_height : $height; 117 $width = $img_width < $width ? $img_width : $width; 118 } 119 120 //拼接缩略图路径 121 $Image->resize($width, $height); 122 $save_name_s = $file_name . '_s.' . $postfix; 123 $save_path_s = $save_dir . $file_dir . '/' . $file_date . '/' . $save_name_s; 124 $file_path_s = $request->root() . '/' . $save_path_s; 125 126 if($watermark != 1) { 127 //添加缩略图水印 128 $news_width = $Image->width(); 129 $news_height = $Image->height(); 130 if($news_width > 100) { 131 $Image->text('@ u/'. $user_id, $news_width - 40 - $length * 10, $news_height - 24, function($font) { 132 $font->file('public/foos.ttf'); 133 $font->size(14); 134 $font->color('#ffffff'); 135 }); 136 } 137 } 138 139 //保存缩略图 140 $Image->save($save_path_s, 100); 141 $file_size_s = round($Image->filesize() / 1024 ,2) . 'K'; 142 } 143 144 $file_path = $request->root() . '/' . $save_dir . $save_path; 145 $file_size = round($file-> getClientSize() / 1024 ,2) . 'K'; 146 $file_info[] = compact( 147 'save_name', 'file_size', 'file_path', 'save_name_s', 'file_size_s', 'file_path_s' 148 ); 149 } 150 151 if(empty($file_info)) { 152 return response()->json(['code' => 1010, 'msg' => '异常出错']); 153 } else { 154 return response()->json(['code' => 0, 'msg' => '', 'data' => $file_info]); 155 } 156 }
结束语:
以上是我学习中遇到的一部分问题,不对之处欢迎指正,这篇文章只是说了日志和上传,之后会持续更新,包括路由,中间件,容器等等,还有好多须要说的。