优化这个类库的初衷咱就不放在篇头来说了,直接进入主题。
安装和更新类库,见这里:
首先PHP没有明确的包概念,这个是借鉴了node;php只有调用文件的概念,需要这么做
include 'path/file.php';
那么问题来了,这样会导致可能重复引用一个文件,还会报错
于是PHP做了一个解决的办法
include_once 'path/file.php';
这样就解决了重复引用一个文件的问题,那么问题来了,这样做效率太差
于是就有了后来的spl的概念,而lv这个类库早前设计的时候就是按照psr的标准,用spl+namespace的方式来调用类文件,像这样
// 来自lv类库中的http的请求方法 use lv\request\HTTP; $http = new HTTP('http://levi.yii.so');
他实现了几个优点:
那么问题来了,每次我都需要使用一个长长的use 路径,这些路径你都能记住吗?
现在可以直接引用一个包,简化了类路径容易记住,像这样
// 直接获取HTTP对象 lv('http')->create('http://levi.yii.so'); // 或者是先引一个包,然后再声明不同的HTTP对象 $http = lv('http'); $http->create('http://levi.yii.so')->get(); $http->create('http://levi.yii.so')->post(array('name' => 'levi'));
这个方法参考了node中的require:
var utils = require('./utils');之所以没有使用require命名是因为:
- 这个名称是PHP保留函数
- 期间有考虑过import等诸如此类的名称,不过都没用,因为太多类库有类似名称了,会造成名称重复而引用报错
- 使用类库名称作为函数名更能体现类库特点
目前包含类库中的类,如下:
后续会根据更新情况继续添加其他包
lv('app\config\App')->create()->get();
加载包之后进行create将会返回相应的对象,之后就可以调用这个对象的方法了
lv('app\config\App')->stat('get', 'char');
这段代码的意思是:
// 三种不同形态的常量 namespace appconfig { define('appconfigPOWER', '0755'); const USER = 'root'; class App { const NAME = 'levi'; } } lv('appconfigApp')->cont('NAME'); // 获取类中的常量 lv('appconfig')->cont('USER', true); // 获取命名空间中的常量 lv('appconfig')->cont('POWER', true); // 获取definde中的常量
注意哦:
- 获取类以外的常量,需要在cont方法增加第二个参数:true
- 如果是通过define设置的常量是不受命名空间影响的,如果需要使用命名空间作为常量,需要在define中以命名空间设置常量名
这个我在网上找了很多资料关于:PHP自动加载函数,但是至今为止网络上讨论的基本都是“PHP 自动加载类”;所以下面这个“自动加载函数”也算是本人的一点点小创新
lv('appconfig')->func('app_list', 'contab'));
这段代码的意思是:
加载函数和加载类都需要注意:
如果说一个文件中有多个类或函数,请先调用主类名或主函数名(和文件同名的类、函数)之后再调用同文件下的其他类、函数,如下:
// appconfigApp.php namespace appconfig { class App {} function test(); } // boot.php lv('appconfigApp')->create(); /* * 注意哦,一个文件中多个类和函数 * 一定先通过create声明文件同名对象后,再调用文件中其他的类和函数 */ lv('appconfig')->func('test');
注意哦,请尽量保持一个文件中只有一个同名类或者是一个同名函数~
由于PHP本身文件限制,通过调用包的方式加载PHP,是无法获取指定PHP中的变量值,无论这个变量是布尔、数值、字符串这样的弱类型,还是Object这样的复杂类型,均无法获取。如果说你一定要调用PHP中的特定变量,可以将变量通过:函数、类中的方法、静态方法返回变量值。
在NodeJS中,通过require调用模块,是通过将所有的对象引用在exports这个对象上,例如:
var PI = Math.PI; exports.area = function (r) { return PI * r * r; }; exports.circumference = function (r) { return 2 * PI * r; };
而设计lv这个类库的时候我并没有采用这个方式,原因很简单,我需要做的是一个PHP的类库,而不是再造一个语言出来,我不希望使用的人脱离了这个类库后,代码就无法执行了,你可以试着想想,如果我有这么$exports这个对象,离开了这个类库后随来解析这个对象,这只能额外增加麻烦。
再次提醒:如果一个文件中同时存在常量、函数、类,请一定要先create文件同名的对象后,再去加载其他对象数据,例如:
// Applist.php namespace appconfig { const NUM = '50'; function getAll($type) {} class Applist { const NAME = 'levi'; public function say() {} public static function power() {} } } namespace action; // 先调用包,并通过create对象 $app = lv('appconfig'); $list = $app->create(); // 调用包中的函数、常量 $app->func('getAll', 'studio'); $app->cont('NUM', true); // 调用文件同名类中的方法、静态方法、常量 $list->say(); $list->cont('NAME'); $list->stat('power');
除了lv类库中自定义的包以外,也可以接受自定义添加包名称;例如上面的示范代码中,均是来自命名空间:appconfig;那么我可以将这个命名空间重新命一个简单的名字,例如:
lv('appconfig')->save('conf'); // 之后我只需这样子就可以了 lv('conf', 'App')->create(); lv('conf', 'App')->CONT('NAME'); lv('conf')->func('test'); // 也可以保存完整路径包 lv('appconfigApp')->save('app'); lv('app')->create();
注意哦,一旦调用执行save成功后,只要是当前执行进程下,无论什么文件、什么位置,都可以使用包名称,加载对象、方法、常量、函数、静态方法等等,当前文档便于阅读,都写在一块。
当然,你也可以使用同一个对象来处理,这样效率更高,例如:
$conf = lv('appconfig')->save('conf'); $conf->create();
注意哦,所有命名空间、参数均是区分大小写的哦,千万不要将appconfigApp和appconfigapp弄混淆咯
使用上面那个方法能极大减轻我们繁琐的命名空间引用,但是他仍旧存在一个问题:他需要命名空间啊!!很多朋友写代码的时候根本就没有写命名空间的习惯,那么没有命名空间怎么办?怎么才能将function a、class b从目录lib、mode、inc中找出来?
要做到这个也不难,在lv的save方法中,定义了第二个参数,将其设置成true,系统将会自动将“路径”当作命名空间,假设apptincaction目录下有我们需要加载的文件,代码如下:
$tinc = lv('apptincaction')->save('tinc', true); $tinc->func('app_list', time()); lv('tinc', 'Tinc')->create(); // 将下面这个文件保存为一个新的包 $onic = lv('tinc', 'Oinc')->save('onic', true); $onic->create()->say(); $onic->cont('TEST');
小提示,拿上面示范代码举例:
apptincaction目录下所有文件都没有命名空间(namespace),不过apptincactionOnic.php却有命名空间;这样有问题吗?其实不用担心这样的问题,因为lv类库在加载的时候会根据情况进行判断哦~
上面提到了将存命名空间和路径的概念,有几个地方需注意:
原因很简单:lv类库中所有的系统默认包不允许替换、删除和修改
解决办法:
lv('path')->inPack('file_path');
返回true则已存在相同的包名称;如果没有提供参数,将返回所有包及包对应的命名空间和路径
小提示:虽然不可以重复命名相同的包,但是可以将一个命名空间、路径命名成多个包名,例如:
lv('path')->save('file_path');将命名空间、路径重复命名的时候,注意不可将命名空间作为路径的方式记录在包中,也不可以将路径作为命名空间记录在包中
分两种情况:
解决方法:
// 第一种方法,使用use来获取Node对象 use lvloadNode; $node = new Node($pack); // 第二种方法,老老实实写命名空间 use lvrequestHTTP; $http = new HTTP('http://levi.yii.so');
解决方法:
- 尽量不要写和第三方类库相同的函数名,请自行搜编码规范
- 使用命名空间解决
完全可以,而且不用担心是否会重复加载的问题。
use lvrequestHTTP; new HTTP(); lv('http')->create();
不可以,继承类需要写上完整的命名空间地址
class Http_muit extends lvrequestHTTP; // 或者通过use的方式 use lvrequestHTTP; class Http_muit extends HTTP;
2014.11.4,问的人不少,这里统一回复。
首先说下lv这个方法是一种加载方式,而composer是类库管理。lv这个类库也可以使用composer在线安装、升级。
打个不是特别形象的比喻,就好比php的opcache是缓存代码的,而redis是缓存数据的,两者是不一样的。
之后有朋友说,composer不是有autoloader嘛?
composer的autoloader是通过PSR的规范,需要写命名空间或者是配置composer.json的。而lv的类包加载目的就在于即便你不按照规范,就能以OO的方式加载对象,就像include一样简单
// include include 'app/config/App.php'; // lv.node lv('appconfigApp')->create();
而至于为什么不直接include,以及PSR方式来使用,请移步至这篇文档的篇头,有对比说明
在更新类库之前其实一直有个想法,能否简化类库的包引用。我一直认为使用“命名空间”的方式编写代码是一件很规范的事情。后来我接触了Laravel这个PHP框架,这个框架通过大量的静态方法实现了外部调用的接口,而避免使用大量的命名空间这么繁琐的事情,从而引起了之前简化调用类库的念头。
最近一直在使用node,也写了一个自己的前端类库:“lv-js”,有兴趣的朋友可以看看这里:http://levi.yii.so/archives/3690
我意识到:
其实早前在设计这个类库的加载方法的时候,已经考虑到了直接调用对象,参见:lvloadL::loadObj();所以只要在这个类的基础上构建一个子类即可,于是衍生出了lvloadNode,这个类继承自:lvloadL
namespace lvload { use lvfilePath; class Node extends L { // ...
不过如果是以对象作为外部调用的话,仍旧未达到简化的目的,于是我在类库的根目录建立了一个函数文件:lv.php;代码很简单,以根节点命名空间返回Node对象;代码如下:
function lv($package, $subname = '') { return new lvloadNode($package, $subname); }
至此完毕,就这么简单。如果你感兴趣,不妨留言给我提出宝贵建议。结束前在送上一枚安装类库的方法(包含在线、离线下载安装)
安装和更新类库,见这里:
您可能也喜欢: | ||||
通过 Google Docs 加密你的 Gmail 邮件 |
linux使用crontab实现PHP执行定时任务 |
初识 jQuery Deferred |
一句话删除目录下所有文件 |
开发者:为自己的App制作一个专属网页吧 |
无觅 |