-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 55.1 KB
/
content.json
1
[{"title":"PHP7新特性总结","date":"2017-09-28T06:43:41.000Z","path":"posts/uncategorized/2017-09-28-PHP7新特性总结.html","text":"为什么PHP7比PHP5性能提升了? 变量存储字节减小,减少内存占用,提升变量操作速度 改善数组结构,数组元素和 hash 映射表被分配在同一块内存里,降低了内存占用、提升了 cpu 缓存命中率 改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率 具体见:官方文档 标量类型申明 类型声明允许函数在调用时要求参数为特定类型。 如果给出的值类型不对,那么将会产生一个错误: 在PHP 5中,这将是一个可恢复的致命错误,而在PHP 7中将会抛出一个TypeError异常。为了指定一个类型声明,类型应该加到参数名前。这个声明可以通过将参数的默认值设为NULL来实现允许传递NULL。 php5支持的类型有Classname、interfacename、self、array、callable。 php7新增的类型有bool、float、int、string。 具体见:类型申明 返回值类型申明1234567<?phpfunction arraysSum(array $arrays): array{ return $arrays;}$re = arraysSum([1, 2]);//这里参数必须填数组var_dump($re); 上面的函数申明参数和返回值为array,参数里为非数组运行就会报错。 具体见:返回值类型申明 null合并运算符 由于日常使用中存在大量同时使用三元表达式和 isset()的情况, 我们添加了null合并运算符 (??) 这个语法糖。如果变量存在且值不为NULL, 它就会返回自身的值,否则返回它的第二个操作数。 1234567//($_GET['user']不存在$username为nobody)// php5写法$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';// PHP7写法$username = $_GET['user'] ?? 'nobody';// PHP7写法($_GET['user']不存在获取$_POST['user'],还不存在为 'nobody')$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody'; 组合比较符 太空船操作符用于比较两个表达式。当$a小于、等于或大于$b时它分别返回-1、0或1。 比较的原则是沿用 PHP 的常规比较规则进行的。 1234<?phpecho 1 <=> 1; // 0echo 1 <=> 2; // -1echo 2 <=> 1; // 1 通过 define() 定义常量数组 Array 类型的常量现在可以通过 define() 来定义。在 PHP5.6 中仅能通过 const 定义。 匿名类 现在支持通过new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。 详细可参考匿名类 Unicode codepoint 转译语法 这接受一个以16进制形式的 Unicode codepoint,并打印出一个双引号或heredoc包围的 UTF-8 编码格式的字符串。 可以接受任何有效的 codepoint,并且开头的 0 是可以省略的。 Closure::call() Closure::call() 现在有着更好的性能,简短干练的暂时绑定一个方法到对象上闭包并调用它。 123456789101112<?phpclass A {private $x = 1;}// PHP 7 之前版本的代码$getXCB = function() {return $this->x;};$getX = $getXCB->bindTo(new A, 'A'); // 中间层闭包echo $getX();// PHP 7+ 及更高版本的代码$getX = function() {return $this->x;};echo $getX->call(new A);//以上会输出 1 1 为unserialize()提供过滤 这个特性旨在提供更安全的方式解包不可靠的数据。它通过白名单的方式来防止潜在的代码注入。具体见unserialize() 会话选项 session_start() 可以接受一个 array 作为参数, 用来覆盖 php.ini 文件中设置的 会话配置选项。在调用 session_start() 的时候, 传入的选项参数中也支持 session.lazy_write 行为, 默认情况下这个配置项是打开的。它的作用是控制 PHP 只有在会话中的数据发生变化的时候才 写入会话存储文件,如果会话中的数据没有发生改变,那么 PHP 会在读取完会话数据之后, 立即关闭会话存储文件,不做任何修改,可以通过设置 read_and_close 来实现。","tags":[{"name":"php work","slug":"php-work","permalink":"http://www.wangwenfan.com/tags/php-work/"}]},{"title":"javascript 分片上传文件demo","date":"2017-09-27T10:01:26.000Z","path":"posts/uncategorized/2017-09-27-javascript-分片上传文件demo.html","text":"文件上传是一个使用频率非常高的一个功能。但是项目中总觉得它不够完善。从而暴露出一些细节问题。于是动手写一个分片上传的demo分享出来供大家参考。 实现方式 JavaScript有个slice函数可以截取文件指定片段大小,然后把文件写入FormData,与普通的 Ajax 相比,使用 FormData 的最大优点就是我们可以异步上传二进制文件。123456789$.ajax({ url: '/upload', type: 'POST', cache: false, data: FormData, processData: false, contentType: false }).done(function(res) {}).fail(function(res) {}); processData设置为false。因为data值是FormData对象,不需要对数据做处理。contentType设置为false。因为是由<form>表单构造的FormData对象,且已经声明了属性enctype="multipart/form-data",所以这里设置为false。 上传过程中把分片信息存储到LocalStorage。还有一种方式是,上传时到服务器去看有没有这个文件,再取回大小。根据这个大小找到当前未上传完的文件的起始位置。当前demo主要用第一种方式实现。 服务端用PHP来实现。php的file_put_contents()函数可以追加写入文件。第一个参数为写入路径,第二个为文件,第三个参数FILE_APPEND可以追加写入。具体使用查看:http://php.net/manual/zh/function.file-put-contents.php 目录结构123456789|-breakpoint|_____static| |___common.js 上传逻辑| |__style.css 模板基本样式|_____upload 附件目录||_____demo.html 模板||_____fileTest.php 服务端文件 使用方法 下载或clone下来,访问/demo.html即可。 初始化,commo.js里已有。123456//初始化UP.__init({ myFile: \"#myFile\", //fileinput节点 ServerUrl:\"/fileTest.php\",//服务器地址 eachSize:1024 //分片大小}); 下载地址https://github.com/wangwenfan/breakpoint","tags":[{"name":"php javascript work","slug":"php-javascript-work","permalink":"http://www.wangwenfan.com/tags/php-javascript-work/"}]},{"title":"用WebUpload实现简单文件上传功能","date":"2017-09-06T09:23:44.000Z","path":"posts/uncategorized/2017-09-06-用WebUpload实现简单文件上传功能.html","text":"这个图片上传工具可以配置成全局使用,包括单图上传、多图上传、视频上传、音频上传、编辑器图片视频上传、音频上传带播放功能。带附件历史记录功能。编辑器使用百度Ueditor开发。移动端上传为Weui改造。) 使用方法 一种为Thinkphp3.2集成,直接可以使用(需配置在项一级目录,二级目录会报错)。另一种为纯前端使用的jsdk。 1.PHP使用方法 pc端访问 yourpath/home/index/index 移动端访问 yourpath/home/index/mobileindex项目配置文件 修改 /resource/js/app/config.js 修改 /Application/Common/config.php核心文件 新增了 /ThinkPHP/Library/Org/Util/UploadFile.class.php 修改了 /ThinkPHP/Think/Upload/Driver/Qiniu.class.php 数据库 附件表,可按需修改1234567891011121314CREATE TABLE `attachment` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID', `file_type` enum('1','2','3','4') NOT NULL COMMENT '附件类型1图片2音频3视频4其他', `file_name` varchar(100) NOT NULL COMMENT '附件名', `file_url` varchar(200) NOT NULL COMMENT '附件地址', `is_cdn` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否在cdn,1是0否', `file_size` int(11) NOT NULL DEFAULT '0' COMMENT '附件大小', `year` smallint(6) NOT NULL COMMENT '上传年份', `month` tinyint(2) NOT NULL COMMENT '上传月份', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', PRIMARY KEY (`id`), KEY `year` (`year`) USING BTREE, KEY `month` (`month`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8; 图片上传函数参数 $name 表单字段的名称,同一页面不能为空 $value 表单input值 $default 默认显示的缩略图 $options 图片上传配置信息 单图上传 1tpl_form_field_image($name, $value = ‘’, $default = ‘’, $options = array()) 多图上传 1tpl_form_field_multi_audio($name, $value = array(), $options = array()) 音频上传、视频上传类似,具体参考/Application/Common/Common/function.php 2. JSDK使用方法 jsdk访问地址 yourpath/webTest/index.html 只需要把里面的webTest目录文件取出来就行。 修改配置webTest/assets/js/EasyUpload.js,如下所示: 12345678910111213141516//项目url地址var app_path = 'http://xxx';//定义路由var appConfigPath = { //上传地址 uploadPath:app_path+"index.php/home/index/upload", //历史图片 imgList:app_path+"index.php/home/index/imageList", //展示远程图片地址 fetchImage:app_path+"index.php/home/index/fetch", //历史视频 vedioList:app_path+"index.php/home/index/videoList", //历史音频 audioList:app_path+"index.php/home/index/audioList", deleteFile:app_path+"index.php/home/index/deleteFile"}; 调用方式,参照webTest/index.html引入css和js文件,再调用需要上传的方法,方法里的参数有 domName 节点名、defaultValue 默认值、options 配置 。具体参数使用方式查看webTest/assets/js/EasyUpload.js 如下所示: 123456789101112<script> $(function () { //图片上传 EasyUpload.loadFiledImage("#filedImage"); //编辑器 EasyUpload.loadUeditor("#content"); //音频上传 EasyUpload.loadAudio('#audios'); //视频上传 EasyUpload.loadVideo('#movies'); });</script> 文件上传返回格式: 1234567891011{"ext": "gif","name": "jdfw.gif","attachment": "/Uploads/image/2017-09-06/59afb44c54ef4.gif","filename": "/Uploads/image/2017-09-06/59afb44c54ef4.gif","url": "http://10.10.12.232:99/Uploads/image/2017-09-06/59afb44c54ef4.gif","is_image": 1,"filesize": 1280038,"height": 80,"widch": 80} 历史记录返回格式(items的key为id的值) 1234567891011121314151617181920{"message": {"message": {"items": {"1": {"id": "1","filename": "cms结构_v1.png","attachment": "/Uploads/image/2017-09-06/59afb1fbeeaf1.png","url": "/Uploads/image/2017-09-06/59afb1fbeeaf1.png","type": "1","createtime": "2017-09-06"}},"page": ""},"error": 0},"redirect": "","type": "ajax"} 下载地址https://github.com/wangwenfan/EasyUpload","tags":[{"name":"work web php","slug":"work-web-php","permalink":"http://www.wangwenfan.com/tags/work-web-php/"}]},{"title":"PHP观察者模式","date":"2017-07-21T08:36:13.000Z","path":"posts/uncategorized/2017-07-21-PHP观察者模式.html","text":"观察者模式(Observer),当一个对象的状态发生改变时,依赖他的对象会全部收到通知,并自动更新。一个事件发生后,要执行一连串更新操作.传统的编程方式,就是在事件的代码之后直接加入处理逻辑,当更新得逻辑增多之后,代码会变得难以维护.这种方式是耦合的,侵入式的,增加新的逻辑需要改变事件主题的代码观察者模式实现了低耦合,非侵入式的通知与更新机制。 1. 传统的使用方式1234567891011121314151617181920class Order{ // 订单状态 private $state = 0; // 订单状态有变化时发送通知 public function addOrder() { $this->state = 1; // 发送邮件 Email::update($this->state); // 短信通知 Message::update($this->state); // 记录日志 Log::update(); // 其他更多通知 }}$order = new Order();$order->addOrder(); 就会同时产生三个通知:发送邮件、发送短信和记录日志。在系统小的时候,这是非常快捷有效的方式。可是,当系统变大的时候,这种方法马上面临难以扩展的问题,并且容易出错。 如果订单不需要某种通知,比如不需要记录日志,则必须修改Order类,做状态的判断; 如果再加一种通知方式,如系统消息通知,则除了增加新类,同时还需要修改Order类和客户端。 这两条都不符合面向对象中的开闭原则,会让系统越来越难维护。 2. 使用观察者模式来实现这个功能123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122<?php/** * 被观察者接口 */interface Observable{ // 添加/注册观察者 public function attach(Observer $observer); // 删除观察者 public function detach(Observer $observer); // 触发通知 public function notify();}/** * 观察者接口 */interface Observer{ //收到通知处理方法 public function update(Observable $observable);}/** * 被观察者 * 职责:添加观察者到$observers属性中, * 有变动时通过notify()方法运行通知 */class Order implements Observable{ // 保存观察者 private $observers = array(); // 订单状态 private $state = 0; // 添加(注册)观察者 public function attach(Observer $observer) { $key = array_search($observer, $this->observers); if ($key === false) { $this->observers[] = $observer; } } // 移除观察者 public function detach(Observer $observer) { $key = array_search($observer, $this->observers); if ($key !== false) { unset($this->observers[$key]); } } // 遍历调用观察者的update()方法进行通知,不关心其具体实现方式 public function notify() { foreach ($this->observers as $observer) { // 把本类对象传给观察者,以便观察者获取当前类对象的信息 $observer->update($this); } } // 订单状态有变化时发送通知 public function addOrder() { $this->state = 1; $this->notify(); } // 获取提供给观察者的状态 public function getState() { return $this->state; }}/** * 观察者发送邮件 */class Email implements Observer{ public function update(Observable $observable) { $state = $observable->getState(); if($state){ echo "发送邮件:您已成功下单<br>"; }else{ echo "发送邮件:错误;下单失败<br>"; } }}/** * 观察者发送短信 */class Message implements Observer{ public function update(Observable $observable) { $state = $observable->getState(); if($state){ echo "发送短信:你已下单成功<br>"; }else{ echo "发送短信:错误;下单失败<br>"; } }}/** * 客户端使用 */$email = new Email;$message = new Message;//创建订单$order = new Order;$order->attach($email);$order->attach($message);$order->addOrder();// 删除记录日志观察者$order->detach($email);$order->detach($message); 如果我需要新增一个观察者,只需要添加观察者本身的类,再实现update方法。1234567891011121314/** * 观察者:系统消息 */class Alert implements Observer{ public function update(Observable $observable) { echo '系统消息:您的订单有更新了~~~'; }}// 创建“系统消息”观察者$alert = new Alert();// 注册观察者到订单对象中$order->attach($alert); 3. 总结 在观察者模式中,被观察者完全不需要关心观察者,在自身状态有变化是,遍历执行观察者update()方法即完成通知。 在观察者模式中,被观察者通过添加attach()方法,提供给观察者注册,使自己变得可见。 当被观察者改变时,给注册的观察者发送通知。至于观察者如何处理通知,被观察者不需要关心。 这是一种良好的设计,对象之间不必相互理解,同样能够相互通信。","tags":[{"name":"work php 设计模式","slug":"work-php-设计模式","permalink":"http://www.wangwenfan.com/tags/work-php-设计模式/"}]},{"title":"自己写PHP框架需要哪些知识","date":"2017-07-17T05:49:19.000Z","path":"posts/uncategorized/2017-07-17-自己写PHP框架需要哪些知识.html","text":"原文链接:https://www.zhihu.com/question/26635323/answer/33812516 很多人当听到别人要开发框架的时候第一想法就是,又重复造轮子。其实造轮子的过程是一个快速积累知识的过程,能较快的发现自己的不足,以及学到一些自己未发现的知识点。所以建议在有一定PHP基础的情况下,都去尝试完成一个五脏俱全的基本框架吧。 那么下面分享一下我之前造轮子的过程吧: 尝试试用一个以上的框架,看完一遍使用文档,目的在于了解它都有哪些功能,发现他的一些比较好的用法设计; 在看文档的同时并行的思考这一部分的实现原理,如果想不明白则记下来或者上Github去看源码(当然在不熟悉代码结构的情况下可能很难找,所以我建议还是先记下来); 看它的项目结构,同时思考这个结构的意义,比如现在大部分框架都把入口文件与静态资源单独放到public目录里与其它目录分开的原因是什么; 尝试看一遍源码的运行流程,从入口到输出,以及错误处理,模板引擎,配置等多个点了解一下(如果基础允许的话,在这过程中解决上面记下来的疑点吧); 开始自己实现吧,先写一个大概的功能列表,把你要实现的点写出来; 然后思考如果组织代码结构,在没有太多实践经验的情况下就按你最熟悉的方式组织吧(先实现再优化); 先跑通基本的hello world!; 一点点加功能吧; 发现不足,改进它; 这里在加功能的时候,尽量自己实现所有的能实现的组件,比如文件上传,错误处理等等,毕竟目的是实习知识。那么这过程其实走下来对于基础稍差的人来说可能会特别不顺利,那么没关系,遇到哪一个点卡住,先解决你对这个点的问题,快速补充知识再回来继续。 总结一些在造框架过程中通常会用到的一些点吧(以下排列没有先后顺序): MVC 自动加载: PHP: 自动加载类; 错误处理:http://php.net/manual/zh/book.errorfunc.php; PHP标准库 (SPL)PHP: SPL - Manual; 输出缓冲控制: PHP: 输出控制; PHP 选项/信息:PHP 选项/信息; 数据库抽象层:PHP: 数据库抽象层; session拓展:PHP: Session 扩展; 反射:http://php.net/manual/zh/book.reflection.php; 类和对象:PHP: 类/对象; 图像处理和 GD:PHP: GD - Manual; 邮件相关的SMTP; 文件系统:PHP: Filesystem; 预定义变量:PHP: 预定义变量; 字符串处理:PHP: 字符串 - Manual; 正则表达式: http://php.net/manual/en/book.pcre.php; 基本的可能上面这些也足够了,虽然上面给的每一个链接都有很多内容,但是掌握常用的就好,可以结合搜索引擎去了解。如果你想给框架加一些更巧妙的,或者更丰富的功能的话,这里还有一些知识点: 常见的设计模式:工厂、单例,外观、观察者等; 迭代器等预定义接口:PHP: 预定义接口; 数据库拓展:PHP: 数据库扩展; 国际化与字符编码支持 PHP: 国际化与字符编码支持; 常用的缓存,Redis, Memcache,Apc等; 队列服务如ActiveMQ,Beanstalkd等; 多数据库支持如Mongo; 事件与钩子; 另外还有一种创建框架的方式,不过这里用“创建”已经不太合适了,叫组合框架吧,那就是使用Composer基于开源组件拼装一个属于自己的框架。当然这里不建议新手这么干,这可能会让你在很多基础的东西上得不到锻炼。如果个人技术能力已经比较成熟了,目的在于快速开发项目的时候,用它绝对是利器。 最后推荐一些参考框架: Slim Framework - 微框架,一个框架基本功能都满足了,很适合用于学习; Silex - 微框架,基于Symfony2组件; CodeIgniter - 结构很清晰的PHP框架;","tags":[{"name":"work php","slug":"work-php","permalink":"http://www.wangwenfan.com/tags/work-php/"}]},{"title":"javaScript总结","date":"2017-07-12T08:35:21.000Z","path":"posts/uncategorized/2017-07-12-javaScript总结.html","text":"微博批量取消关注: 获取radio的选中值 json格式化函数 打开新窗口函数: 刷新回到滚动条位置 Ajax async参数 ajax异步上传文件 javaScript实现img图片加载失败后显示默认图片 ajax请求加载状态 javaScript cookie对象封装 Ajax请求对象封装 javascript LocalStorage 封装 微博批量取消关注:1234567891011$(".btn_link.S_txt1").click(); // 进入“批量管理”// 选中全部var lis = $(".member_li.S_bg1").parentNode.childNodes;for(var i=0;i<lis.length;i++){ lis[i].className = "member_li S_bg1 choosed S_link1_br";}// 为了让“取消关注”可点击$(".member_li.S_bg1").click();$(".member_li.S_bg1").click();$(".W_btn_a[node-type='cancelFollowBtn']").click(); // 取消关注$(".W_btn_a.btn_34px[node-type='ok']").click(); // 确定 获取radio的选中值123456789function getValue(name){ // method 1 var radio = document.getElementsByName(name); for (i=0; i<radio.length; i++) { if (radio[i].checked) { return radio[i].value; } }} json格式化函数123456789101112131415161718192021222324252627282930<style> pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; } .string { color: green; } .number { color: darkorange; } .boolean { color: blue; } .null { color: magenta; } .key { color: red; }</style>function syntaxHighlight(json) { if (typeof json != 'string') { json = JSON.stringify(json, undefined, 2); } json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return json.replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function(match) { var cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '<span class="' + cls + '">' + match + '</span>'; });} 打开新窗口函数:123456function openwinx(url,name,w,h) { if(!w) w=screen.width-4; if(!h) h=screen.height-95; url = url+'&pc_hash='+pc_hash; window.open(url,name,"top=100,left=400,width=" + w + ",height=" + h + ",toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no");} 刷新回到滚动条位置123456789101112131415161718192021window.onbeforeunload = function () { var scrollPos; if (typeof window.pageYOffset != 'undefined') { scrollPos = window.pageYOffset; } else if (typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat') { scrollPos = document.documentElement.scrollTop; } else if (typeof document.body != 'undefined') { scrollPos = document.body.scrollTop; } document.cookie = "scrollTop=" + scrollPos; //存储滚动条位置到cookies中}window.onload = function () { if (document.cookie.match(/scrollTop=([^;]+)(;|$)/) != null) { var arr = document.cookie.match(/scrollTop=([^;]+)(;|$)/); //cookies中不为空,则读取滚动条位置 document.documentElement.scrollTop = parseInt(arr[1]); document.body.scrollTop = parseInt(arr[1]); }} Ajax async参数 async值默认为true,当值为false时为同步请求可以获取到ajax的返回值。 ajax异步上传文件12345678910111213141516171819202122232425262728293031$('#btn').click(function () { var userName = document.myForm.userName.value; var img = document.myForm.img.files[0]; var fm = new FormData(); fm.append('userName', userName); fm.append('img', img); $.ajax( { url: 'submitform.php', type: 'POST', data: fm, contentType: false, //禁止设置请求类型 processData: false, //禁止jquery对DAta数据的处理,默认会处理//禁止的原因是,FormData已经帮我们做了处理 success: function (result) { //测试是否成功//但需要你后端有返回值 alert(result); } } );});$.ajax({ url: '/upload', type: 'POST', cache: false, data: new FormData($('#uploadForm')[0]), processData: false, contentType: false}).done(function(res) {}).fail(function(res) {}); javaScript实现img图片加载失败后显示默认图片123$('img').error(function () { $(this).attr('src', http+"statics/shuozy/images/nopic.gif"); }); ajax请求加载状态1234567$(document).ajaxStart(function(){ //ajax请求开始 $("yourDom").html('加载中').show();}).ajaxStop(function(){ //ajax请求结束 $("yourDom").hide();}); javaScript cookie对象封装1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/** * JavaScript COOKIE封装 * @type {{set: cookie.set, get: cookie.get, remove: cookie.remove, clear: cookie.clear, getCookies: cookie.getCookies}} */var cookie={ //获取cookie对象,以对象表示 getCookiesObj:function () { var cookies = {}; if (document.cookie) { var objs = document.cookie.split('; '); for (var i in objs) { var index = objs[i].indexOf('='), name = objs[i].substr(0, index), value = objs[i].substr(index + 1, objs[i].length); cookies[name] = value; } } return cookies; }, //设置cookie set: function (name, value, opts) { //opts maxAge, path, domain, secure if (name && value) { var cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value); //可选参数 if (opts) { if (opts.maxAge) { cookie += '; max-age=' + opts.maxAge; } if (opts.path) { cookie += '; path=' + opts.path; } if (opts.domain) { cookie += '; domain=' + opts.domain; } if (opts.secure) { cookie += '; secure'; } } document.cookie = cookie; return cookie; } else { return ''; } }, //获取cookie get:function (name) { return decodeURIComponent(this.getCookiesObj()[name]) || null; }, //清除某个cookie remove:function (name) { if (this.getCookiesObj()[name]) { document.cookie = name + '=; max-age=0'; } }, //清除所有cookie clear:function () { var cookies = this.getCookiesObj(); for (var key in cookies) { document.cookie = key + '=; max-age=0'; } }, //获取所有cookies getCookies:function (name) { return this.getCookiesObj(); }} Ajax请求对象封装123456789101112131415161718192021222324252627282930313233343536373839var App={ /** * ajax请求函数 * @param param */ getHttp: function (param) { var self = this; var settings = $.extend({ type: 'get', async: true, cache: 'false', dataType: 'json' }, param); if (settings.btn) settings.btn.addClass('disabled'); $.ajax({ type: settings.type, cache: settings.cache, async: settings.async, dataType: settings.dataType, data: settings.data, url: http+settings.url, success: function (data) { if (data.Ret == '0') { self[settings.success](data.Data, settings.is_catid); } else { alert(data.Msg); } }, complete: function (data) { if (settings.btn) { settings.btn.removeClass('disabled'); } }, error: function (data) { alert(data.Msg); } }); }} javascript LocalStorage 封装12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576var Cache = { /** * 保存数据集 * @param key * @param val * @returns {boolean} */ setLSs:function (key, val) { var self = this; if (!val) return; var obj_str = self.getLS(key); if (obj_str) { var obj_arr = obj_str.split(","); if (self.in_array(obj_arr, val)) { return false; } else { obj_arr.push(val); self.setLS(key, obj_arr.join(",")); } } else { self.setLS(key, val); } }, /** * 读取数据集 * @param key * @returns {*} */ getLSs:function (key) { var obj_str = this.getLS(key); if (obj_str != null) { return obj_str.split(","); } return obj_str; }, /** * 判断是否存在数组中 * @param arr * @param val * @returns {boolean} */ in_array:function (arr, val) { for (var i = 0; i < arr.length; i++) { if (arr[i] == val) { return true; } } return false; }, /** * 设置缓存数据 * @param key * @param data */ setLS:function (key, data) { if (localStorage.getItem(key) != null) localStorage.removeItem(key); localStorage.setItem(key, data); }, /** * 清除缓存数据 * @param key */ removeLS:function (key) { localStorage.removeItem(key); }, /** * 读取缓存数据 * @param key * @returns {null} */ getLS:function (key) { var v = localStorage.getItem(key); return v === undefined ? null : v; }}","tags":[{"name":"work javascript","slug":"work-javascript","permalink":"http://www.wangwenfan.com/tags/work-javascript/"}]},{"title":"PHPCMSv9整合百度Ueditor","date":"2017-06-15T03:03:46.000Z","path":"posts/uncategorized/2017-06-15-PHPCMSv9整合百度Ueditor.html","text":"phpcms自带的ckeditor不是很好用,想换一个编辑器,网上看了下,就ueditor各方面功能比较全,支持一般的格式功能、预览、格式刷、远程抓取图片。 修改后增加的功能 可抓取所有的远程图片,例如没有图片后缀的图片url。 本地上传图片和远程抓取图片可添加水印功能。 可配置图片上传到本地或七牛、阿里云等CDN。 目录结构 1234567891011121314151617-project|__caches| |__config | |__config.json 服务端配置文件|——phpcms| |__libs| | |__classes| | |__My_form_class.php 覆盖调用编辑器的类| |__modules| |__ueditor 服务端图片上传逻辑文件| |__content| |__fields| |__editor| |__form.inc.php 调用字段数据方法(会生成缓存)|——static| |__js| |__ueditor 百度ueditor前端资源文件 使用方法 下载文件解压到相对应的目录。 按需修改config.json文件里的服务端配置项。 按需修改/phpcms/modules/ueditor/Uploader.class.php 第108行upload_type变量,用它来判断图片上传到本地还是cdn。我是放在系统配置文件里。如果需要上传到指定的cdn,按需再110行写自己的逻辑。 下载地址:https://github.com/wangwenfan/Phpcms_Ueditor","tags":[{"name":"phpcms work php","slug":"phpcms-work-php","permalink":"http://www.wangwenfan.com/tags/phpcms-work-php/"}]},{"title":"yii2框架学习总结(1)","date":"2017-04-14T10:54:39.000Z","path":"posts/uncategorized/2017-04-14-yii2框架学习总结(1).html","text":"这一周工作不是很忙,抽空重新把yii2框架学习了一下。时间不是特别多,应该只掌握了基本部分,记录一下,下次用的时候还可以翻一下。 1.安装 使用composer或者下载归档文件都可以,我使用的是composer在文件目录下执行下面两个命令:composer require "fxp/composer-asset-plugin:^1.2.0"composer create-project --prefer-dist yiisoft/yii2-app-basic basic第一条命令安装 Composer asset plugin, 它是通过 Composer 管理 bower 和 npm 包所必须的。 第二条命令会将 Yii 安装在名为 basic 的目录中。如果你想使用其它目录名称,你可以选择其他目录名称。 2.用户登陆 我是用gii生成了一个admin的模块,使用yii自带的user模型,主要讲下自动登陆和密码生成及验证。 新建一个user表,里面有id,username,password,authkey这几个字段。 app\\models\\User.php里里生成has密码的方法:1234public function setPassword($password) { \\Yii::$app->getSecurity()->generatePasswordHash($password); } 验证密码的方法:1234public function validatePassword($password) { \\Yii::$app->getSecurity()->validatePassword($password,$this->password); } 记住密码,同样是user.php里面,当判断用户是记住密码登陆执行下面方法,讲生成的字符串更新authkey字段,下次用户登陆就会用cookie和authkey进行验证,判断是否记住了密码。12345public function generateAuthKey() { $this->authKey = Yii::$app->security->generateRandomString(); $this->save(); } 使用behaviors进行ACF简单的权限验证 控制器里面写一个行为behaviors方法代码如下123456789101112131415161718public function behaviors() { return [ 'access' => [ 'class' => yii\\filters\\AccessControl::className(), // 使用核心过滤器Access 对执行动作进行验证 'denyCallback' => function ($rule, $action) { //认证失败后回调函数 $this->goHome(); }, 'rules' => [ // 规则 [ 'actions' => [],//所有方法 'allow' => true, // 只允许认证用户进行访问 'roles' => ['@'], //?为游客 @为认证用户 ], ], ] ]; }","tags":[{"name":"work","slug":"work","permalink":"http://www.wangwenfan.com/tags/work/"}]},{"title":"Nginx配置https","date":"2017-02-24T08:44:58.000Z","path":"posts/uncategorized/2017-02-24-Nginx配置https.html","text":"前端时间阿里云免费赠送了一个一年的安全证书,不要白不要干脆就把自己网站弄成https,下面说说自己的实现过程。 首先是下载证书,我的是再阿里云里面,如果购买成功了,直接下载证书。里面会有两后缀分别为.key和.pem的个文件,如下图所示. 下载页面:解压后的文件: 阿里云的下载页面会有使用说明。 先把这两个文件放到你的Nginx目录里的一个新建文件夹中,我是放到nginx/conf/cert/里面在。 修改你的nginx.conf,找到你配置的那个虚拟主机修改如下 1234567891011121314151617server { listen 443; server_name 你的域名; ssl on; root html; index index.html index.htm; ssl_certificate cert/214006921440734.pem; ssl_certificate_key cert/214006921440734.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; location / { root html; index index.html index.htm; }} 使用命令 sudo nginx -t 查看配置是否正确。 如果一切ok的话,我们重新加载下Nginx,sudo nginx reload就可以使用https访问了,但是Nginx是443端口,浏览器会默认访问80端口,所有我们还要配置下80端口的重定向。 123456server { listen 80; server_name 你的域名; rewrite ^(.*) https://$server_name$1 permanent; } 配置好后再重启下Nginx就可以了。","tags":[{"name":"work","slug":"work","permalink":"http://www.wangwenfan.com/tags/work/"}]},{"title":"利用crontab和Shell定时备份数据库和文件","date":"2017-02-22T10:03:25.000Z","path":"posts/uncategorized/2017-02-22-利用crontab和Shell定时备份数据库和文件.html","text":"前两天给客户搭建了一台Linux服务器给做了一个定时备份数据库和网站目录文件夹。下面记录下搭建过程。 1、整个执行过程主要分两步 编写Shell脚本处理整个过程,包括文件导出、打包、删除等。 用crontab定时执行这个脚本。2、shell脚本编写 Shell可以参见 Shell脚本编程30分钟入门。 首先我们写一个导出mysql数据库的脚本,见下面代码。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253#!/bin/bash#备份数据库#Author : wangwenfan#Date : 2017-02-21#配置参数USER=root #数据库用户名PASSWORD=wangwen123 #数据库用户密码DATABASE=test #数据库名称[email protected] #管理员邮箱地址,用以发送备份失败消息提醒BACKUP_DIR=/home/wangwenfan/topons/ #备份文件存储路径LOGFILE=/home/wangwenfan/topons/data_backup.log #日记文件路径DATE=`date '+%Y%m%d' ` #日期格式(作为文件名)AGODATE=`date -d '1 days ago' +%Y%m%d` #前一天DUMPFILE=$DATE.sql #备份文件名ARCHIVE=$DATE.sql.tgz #压缩文件名OPTIONS=\"-u$USER -p$PASSWORD $DATABASE\" #mysqldump 参数 详情见帮助 mysqldump -help#判断备份文件存储目录是否存在,否则创建该目录if [ ! -d $BACKUP_DIR ] ;thenmkdir -p \"$BACKUP_DIR\"fi#开始备份之前,将备份信息头写入日记文件echo \" \" >> $LOGFILEecho \" \" >> $LOGFILEecho \"———————————————–\" >> $LOGFILEecho \"BACKUP DATE:\" $(date +\"%y-%m-%d %H:%M:%S\") >> $LOGFILEecho \"———————————————– \" >> $LOGFILE#切换至备份目录cd $BACKUP_DIR#使用mysqldump 命令备份制定数据库,并以格式化的时间戳命名备份文件mysqldump $OPTIONS > $DUMPFILE#判断数据库备份是否成功if [[ -f $DUMPFILE ]]; then#创建备份文件的压缩包tar czvf $ARCHIVE $DUMPFILE >> $LOGFILE 2>&1#输入备份成功的消息到日记文件echo \"[$ARCHIVE] Backup Successful!\" >> $LOGFILE#删除原始备份文件,只需保 留数据库备份文件的压缩包即可rm -f $DUMPFILEecho \"删除备份原文件成功!\" >> $LOGFILEif [[ -f $AGODATE.sql.tgz ]]; then #statements rm -f $AGODATE.sql.tgz echo \"删除前一天备份文件成功!\" >> $LOGFILE echo \"Database:$DATABASE Daily Backup Successful\" | mailx -s \"数据库备份成功\" $WEBMASTERfielseecho \"Database Backup Fail!(备份失败)\" >> $LOGFILE#备份失败后向网站管理者发送邮件提醒,我安装的是mailxecho \"Database:$DATABASE Daily Backup Fail\" | mailx -s \"数据库备份失败\" $WEBMASTERfi#输出备份过程结束的提醒消息echo \"Backup Process Done(备份完成)\" 3、 脚本中的知识点: if的中的参数:-d 当文件存在并且是一个目录时返回真;-f 当文件存在并且是正规文件时返回真; >>和>符号的使用:“>>” : 如果文件不存在,将创建新的文件,并将数据送至此文件;如果文件存在,则将数据添加在文件后面;“>” : 如果文件不存在,同上,如果文件存在,先将文件清空,然后将数据填入此文件; mailx发送邮件: 我用的是centos,首先安装mailx yum -y install mailx; 安装成功后设置mailx参数,编辑 /etc/mail.rc文件vim /etc/mail.rc; 在文件的末行添加以下文字: 123456set [email protected] #发送邮箱号 set smtp=smtp.139.com #邮件服务器set smtp-auth-user=yourname #邮箱用户名set smtp-auth-password=yourpassword #邮箱密码set smtp-auth=login #登录方式 添加完了后 发送邮件命令为: `echo \"邮箱内容\"| mailx -s \"邮件标题\" 邮箱号 删除上次备份的文件: 上面我设置了个AGODATE=date -d ‘1 days ago’ +%Y%m%d#前一天;如果你需要保留2天的数据,把里面的1改成2就行了,多天以此类推。4、 我们按照上面的方法再编写备份完整目录文件的shell,就没什么好说的了,方法同上,下面是代码。 #!/bin/bash #备份www目录文件夹 #Auther :wangwenfan #Date :2017-02-22 #配置参数 DATA_DIR=www #待备份的文件夹 BACKUP_DIR=/home/wangwenfan/topons/ #备份文件路径 LOGFILE=/home/wangwenfan/topons/wwwroot_backup.log #日志记录文件 [email protected] #管理员邮箱地址,用以发送备份失败消息提醒 DATE=`date '+%Y%m%d' ` #日期格式 F_DATE=`date '+%Y%m%d %H:%M:%S' ` #详细日期格式 AGODATE=`date -d '7 days ago' +%Y%m%d` #上一周备份时间 BACKUP_FILE=${DATA_DIR}${DATE}.tgz #备份压缩文件名 #进入备份文件夹 if [[ ! -d $BACKUP_DIR ]]; then mkdir -p \"$BACKUP_DIR\" fi #开始备份之前将信息写入日志头部 echo \"\" >> $LOGFILE echo \"\" >> $LOGFILE echo \"-----------------\" >> $LOGFILE echo \"BACKUPDATE: $F_DATE ,准备开始备份!\" >> $LOGFILE echo \"-----------------\" >> $LOGFILE #切换至备份的目录 cd $BACKUP_DIR #压缩待备份的目录到该目录下 tar czvf $BACKUP_FILE ../../${DATA_DIR} if [[ -f $BACKUP_FILE ]]; then echo \"Backup Successful (备份文件成功)\" >> $LOGFILE #删除上次备份的文件 if [[ -f ${DATA_DIR}${AGODATE}.tgz ]]; then rm -f ${DATA_DIR}${AGODATE}.tgz echo \"Delete Successful ${DATA_DIR}${AGODATE}.tgz (删除文件成功)\" >> $LOGFILE mail -s \"DATATIME:$F_DATE,$DATA_DIR Backup Successful (项目文件备份成功)\" $WEBMASTER fi #备份成功发送邮件 echo \"DATATIME:$F_DATE,$DATA_DIR Daily Backup Successful (项目文件备份成功)\" | mailx -s \"文件备份成功\" $WEBMASTER echo \"给${WEBMASTER}发送邮件成功\" >> $LOGFILE else echo \"wwwroot Backup fail! (文件备份失败)\" >> $LOGFILE #备份失败后向网站管理者发送邮件提醒,需要mailutils或者类似终端下发送邮件工具的支持 echo \"DATATIME:$F_DATE,$DATA_DIR Daily Backup Fail (项目文件备份失败)\" | mailx -s \"文件备份失败\" $WEBMASTER fi #执行完成 echo \"Script Successful!(脚本执行成功)\" >> $LOGFILE 5、Shell写完了,我们开始设置crontab开始定时任务计划。 crontab介绍 : command 命令参数解释 分 时 日 月 周 命令第1列表示分钟1~59 每分钟用或者 */1表示第2列表示小时1~23(0表示0点)第3列表示日期1~31第4列表示月份1~12第5列标识号星期0~6(0表示星期天)详细使用请看linuxtools介绍,写得很详细。 我们用crontab -l查看当前用户的crontab文件内容,如果不指定用户,则表示显示当前用户的crontab文件内容。然后用crontab -e 编辑crontab文件。 然后重启crontab就可以了,在系统中有service这个命令时:service crond start //启动服务service crond stop //关闭服务service crond restart //重启服务","tags":[{"name":"work","slug":"work","permalink":"http://www.wangwenfan.com/tags/work/"}]},{"title":"迟来的的2016年终总结","date":"2017-01-04T03:12:37.000Z","path":"posts/uncategorized/2017-01-04-迟来的的2016年终总结.html","text":"今年的年终总结本来会迟到的,前两天电脑坏了,还好今天修好了。刚刚看了时间的朋友跨年演讲,里面说到我们在消费时不光要考虑金钱成本,更重要的是时间成本。回顾2016的点滴,喜忧参半,没有什么特别突出的地方。翻出2015年的年终总结,对照当时给自己制定的目标,大部分还是完成了,有一些还在进行中。有几个还没完成的,没有完成的慢慢的就被淘汰了,应该不是自己真正想要的东西,可能是所谓的伪目标吧。 去年告诉自己会跳一次槽,工资涨一点,今年反而跳了两次,年初一次,年尾一次,薪水也超出了自己的预期。还有每个月看一本书,每周看一部电影。由于现在是一名程序员,看技术方面的书籍要多一点,大概看了有四五本的样子。电影从国庆过后就看得比较少了,上半年基本还能保持。而且对美剧产生了兴趣,看了有两三部的样子。专业技能在利用业余时间多多少少有进步了一点,学会了git和svn,并试着用它们管理代码,到后面习惯掌握它们。liunx系统的继续学习,个人搭建公司服务器,使用的centos,自己电脑安装ubuntu使用,基本能达到熟练的效果。在上一个公司对微信开发这一块用得比较多,可以说扩宽了自己的知识面和短板吧。后面自己又和朋友合伙购买了vps,一方面用来科学上网,另一方面当作一个测试服务器。总体来说,2016年自己在工作上有进步,但不是特别大,有一点令自己满意的就是,大的方向是有了,不再像以前那么迷茫了。因为,没有方向,光走是没有意义的。在接下来的2017年需要好好管理自己的时间,技术上的深度和广度都还不够,感觉自己离专业两个字还有很大的差距。应该在数据库、PHP高级技术、linux、JavaScript和css这几方面提升一下。首先得认认真真的把之前买的《JavaScript权威指南》看一篇吧。然后继续坚持写技术博客,坚持阅读,养成一个良好的阅读习惯和保持一个阅读量,先计划一个月一本书吧。希望在写2017年的年终总结的时候能列出这12本书的书单并全部阅读过。 经过一年多的努力,最值得高兴的是各种款也差不多要还完了,预料之中的话下个月的完了。而且从2017起开始记账了,继续使用了以前没坚持下来的随手记。因为现在还是在财富积累的初期阶段,要懂得开源节流。虽然谈不上理财,但是一方面可以养成一个花钱用钱的好习惯,另一方面可以总结出每个月的钱花在哪,有些东西买了到底是不是自己真正想要的。这样坚持记一年,希望在下一次的年终总结中可以晒出一年的账单。 生活方面,这一年回家的次数变多了,这么几年了,今年应该是陪伴家人时间最多的一年吧。情侣之间虽然偶尔也有争吵,有时也会冷战和赌气。但放眼望去,一起走过来的这两三百天时间。其实大家都进步了,可能不够想象中的完美。每次看到她都感觉变成熟了进步了,其实我真的很 开心。至于爱不爱,其实真的没怀疑过。希望自己在2017能改掉一些固执的牛脾气。多用有温度的话语去感染别人。哪怕自己是对的。感情和家庭像工作一样需要用心去经营。 嗯!就这么多,提高自己的专业技术能力,养成阅读的习惯,坚持写博客,坚持记账,多陪伴家人,脾气好一点,用心去热爱生活,虽然生活是那么现实。多挣钱,让自己变得更好,把账单、书单、爱情都晒在2017的总结里。 2016年12月31日","tags":[{"name":"life","slug":"life","permalink":"http://www.wangwenfan.com/tags/life/"}]},{"title":"tp5使用PHPexcel导出文件","date":"2017-01-04T01:37:46.000Z","path":"posts/uncategorized/2017-01-04-tp5使用PHPexcel导出文件.html","text":"tp5使用PHPexcel导出文件一、使用composer安装phpexcel第一种方式:在项目根目录下的composer.json里面添加phpexcel123\"require\": { \"phpoffice/phpexcel\": \"1.8.*\" } 进入composer ,cd到根目录 执行 composer install第二种方式: 直接运行命令php composer.phar require phpoffice/phpexcel 二、封装导出方法1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950function getExcel($fileName, $headArr, $data){ //对数据进行检验 if (empty($data) || !is_array($data)) { die(\"data must be a array\"); } //检查文件名 if (empty($fileName)) { exit; } $date = date(\"Y_m_d\", time()); $fileName .= \"_{$date}.xls\"; //创建PHPExcel对象,注意,不能少了\\ $objPHPExcel = new \\PHPExcel(); $objProps = $objPHPExcel->getProperties(); //设置表头 $key = ord(\"A\"); foreach ($headArr as $v) { $colum = chr($key); $objPHPExcel->setActiveSheetIndex(0)->setCellValue($colum . '1', $v); $key += 1; } $column = 2; $objActSheet = $objPHPExcel->getActiveSheet(); foreach ($data as $key => $rows) { //行写入 $span = ord(\"A\"); foreach ($rows as $keyName => $value) {// 列写入 $j = chr($span); $objActSheet->setCellValue($j . $column, $value); $span++; } $column++; } $fileName = iconv(\"utf-8\", \"gb2312\", $fileName); //重命名表 // $objPHPExcel->getActiveSheet()->setTitle('test'); //设置活动单指数到第一个表,所以Excel打开这是第一个表 $objPHPExcel->setActiveSheetIndex(0); header('Content-Type: application/vnd.ms-excel'); header(\"Content-Disposition: attachment;filename=\\\"$fileName\\\"\"); header('Cache-Control: max-age=0'); $objWriter = \\PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save('php://output'); //文件通过浏览器下载 exit;} 然后调用刚刚封装的方法1234567891011121314public function out(){ //要导出的数据放在一个数组里 $data=array( array('username'=>'zhangsan','password'=>\"123456\"), array('username'=>'lisi','password'=>\"abcdefg\"), array('username'=>'wangwu','password'=>\"111111\"), ); //导出的文件名 $filename=\"test_excel\"; //每一列的标题 $headArr=array(\"用户名\",\"密码\"); $this->getExcel($filename,$headArr,$data);} 后面我们直接调用控制器里的 out方法就可以了!","tags":[{"name":"work","slug":"work","permalink":"http://www.wangwenfan.com/tags/work/"}]},{"title":"常用的设计模式之单例模式","date":"2016-12-29T02:54:39.000Z","path":"posts/uncategorized/2016-12-29-常用的设计模式之单例模式.html","text":"单例模式就是只能让我们实例化一次和创建一个对象的类。像文件类,session类等只需要实例化一次就可以在全局中应用。 1、 非单例模式的普通类,会出现的问题。1234567891011121314151617181920212223<?phpclass Db{ private $instance=null; public function __construct($config=[]) { $dsn = sprintf('mysql:host=%s;dbname=%s', $config['db_host'], $config['db_name']); $this->db = new PDO($dsn, $config['db_user'], $config['db_pass']); }}$config = array( 'db_name' => 'phpcmsv9', 'db_host' => 'localhost', 'db_user' => 'root', 'db_pass' => 'root');$ob=new Db($config);var_dump($ob);$ob2=new Db($config);var_dump($ob2);$ob3=new Db($config);var_dump($ob3);?> 上面会输出的内容为:(1) {1234567891011121314 ["db":"Db":private]=> object(PDO)#2 (0) { }}object(Db)#3 (1) { ["db":"Db":private]=> object(PDO)#4 (0) { }}object(Db)#5 (1) { ["db":"Db":private]=> object(PDO)#6 (0) { }} 每个对象都会占用一个资源,如果实例100次就会占用100次,非常消耗服务器内存和资源。 2、 单例模式例子12345678910111213141516171819202122232425262728293031<?phpclass Test{ private static $instance=null; private $db=null; private function __construct($config=array()) { $dsn = sprintf('mysql:host=%s;dbname=%s', $config['db_host'], $config['db_name']); $this->db = new PDO($dsn, $config['db_user'], $config['db_pass']); } public static function getInstance($config=array()) { if(self::$instance == null) { self::$instance= new self($config); } return self::$instance; }}$config = array( 'db_name' => 'phpcmsv9', 'db_host' => 'localhost', 'db_user' => 'root', 'db_pass' => 'root');$obj=Test::getInstance($config);var_dump($obj);$obj1=Test::getInstance($config);var_dump($obj1);$obj2=Test::getInstance($config);var_dump($obj2);?> 上面的输出内容为:123456789101112131415object(Test)#1 (1) { ["db":"Test":private]=> object(PDO)#2 (0) { }}object(Test)#1 (1) { ["db":"Test":private]=> object(PDO)#2 (0) { }}object(Test)#1 (1) { ["db":"Test":private]=> object(PDO)#2 (0) { }} 我们可以看出用单例模式,每次调用都是返回的同一个对象,获得的对象ID是一样的。我们控制住基类,在源头上限制这个类,使它无法生成多个对象,直接返回。 3、总结 单例模式了违法单一职责原则,因为它自己控制了自己的实例化和生命周期。 单例模式对应用表现为全局状态。全局状态被视为是不良设计,因为所有代码都可以修改它的值。在调试的时候,很难弄清楚全局变量的当前状态是哪一段代码造成的。 在单元测试的时候,单例模式难以测试。如果不进行单元测试,又会影响系统的质量。 单例模式在应用请求的整个生命周期中都有效,这点类似全局变量,会降低程序的可测试性。大部分情况下,也可以用依赖注入来代替单例模式,避免在应用中引入不必要的耦合。 所以,对于仅需生成一个对象的类,首先考虑用依赖注入方式,其次考虑用单例模式来实现。","tags":[{"name":"work","slug":"work","permalink":"http://www.wangwenfan.com/tags/work/"}]},{"title":"hexo 和github搭建个人博客","date":"2016-12-15T07:12:20.000Z","path":"posts/uncategorized/2016-12-15-hexo 和github搭建个人博客.html","text":"一、需要安装的软件1、安装git ubuntu上安装git : sudo apt-get install git windows直接下载git安装包2、安装nodejs ubuntu 上安装node apt install nodejsapt all npm Windows到nodejs官网下载最新版安装包。 二、正式安装hexo 进入你的文件目录 执行以下命令 sudo npm install -g hexo 软件安装完成后初始化它 hexo init 执行成功后就本地就安装完成了,当前目录就是博客的根目录。 生成静态页面 hexo generate 或者 hexo g 本地启动预览 hexo server 或者 hexo s 后面可以加上 --debug 参数查看运行状态,浏览器输入http://localhost:4000 查看本地预览,如果网站打开了说明配置OK。三、配置github 进入你的github,新建一个仓库名字为:名字+github.io 打开你的博客根目录下的 vim _config.yml文件的最后几行,修改 ` 然后执行命令 hexo g hexo d,第一条是重新生成静态页面,第二天是部署到github,执行成功后静态页面就提交到你的github仓库里了。 最后访问你的github地址 xxx.github.io就可以看到效果了。四、绑定域名到github 到你的域名解析后台解析或者修改一个新的域名地址,记录类型为CNAME,记录值为你的github仓库地址:xxx.github.io 如下图所示: 然后在你的博客目录 ->source目录下面新建一个CNAME的文件,内容为你刚刚解析的域名地址。(所以需要提交到github的文件都是放在source这个目录里面的) 再次执行 hexo g和 hexo d就可以了五、hexo常用命令 hexo new 'filename 创建新文章 hexo clean 清除缓存 hexo new page 'filename' 创建新页面 hexo g 生成页面 hexo d部署项目( 生成并部署可以用hexo d -g ) hexo s 开启预览访问后记 如果你想建设一个写文章的bolg的话,用hexo和github真的是一个不错的选择,没有自己搭建服务器的烦恼。可以把维护网站的时间用来多思考,总结,和分享。又比在其他第三方平台上多了一些个性化的功能。反正我觉得挺棒的。","tags":[{"name":"work","slug":"work","permalink":"http://www.wangwenfan.com/tags/work/"}]}]