IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    像node加载包一样加载你的PHP,来自类库:lv

    李惟发表于 2014-11-01 19:30:59
    love 0

    优化这个类库的初衷咱就不放在篇头来说了,直接进入主题。

    安装和更新类库,见这里:

    http://levi.cg.am/archives/3534#install

    lv中类包特点

    首先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.cg.am');

    他实现了几个优点:

    • 按需要加载,所有的对象不是说我使用了use了,就一定会inclued,而是说只有当我程序执行到一个未知调用的时候才会去加载类;这样操作的好处就在于,你可以use成千上百个类文件,但最终去调用、加载的只有当前需要的那么几个文件(对于这点不长篇解释,有兴趣的朋友请查看源文件)
    • 避免重复加载
    • 缓存已加载的文件

    那么问题来了,每次我都需要使用一个长长的use 路径,这些路径你都能记住吗?

    • lv\request\HTTP
    • lv\file\Path
    • lv\data\Queue

    现在可以直接引用一个包,简化了类路径容易记住,像这样

    // 直接获取HTTP对象
    lv('http')->create('http://levi.cg.am');
    
    // 或者是先引一个包,然后再声明不同的HTTP对象
    $http = lv('http');
    $http->create('http://levi.cg.am')->get();
    $http->create('http://levi.cg.am')->post(array('name' => 'levi'));

    这个方法参考了node中的require:

    var utils = require('./utils');

    之所以没有使用require命名是因为:

    1. 这个名称是PHP保留函数
    2. 期间有考虑过import等诸如此类的名称,不过都没用,因为太多类库有类似名称了,会造成名称重复而引用报错
    3. 使用类库名称作为函数名更能体现类库特点

    目前包含类库中的类,如下:

    • event:lv\event\Event
    • event_query:lv\event\EventQuery
    • http:lv\request\HTTP
    • http_proxy:lv\request\HTTP_Proxy
    • path:lv\file\Path
    • queue:lv\data\Queue
    • route:lv\route\Route

    后续会根据更新情况继续添加其他包

    进阶加载方法

    加载对象中的方法

    lv('app\config\App')->create()->get();

    加载包之后进行create将会返回相应的对象,之后就可以调用这个对象的方法了

    加载类中的静态方法

    lv('app\config\App')->stat('get', 'char');

    这段代码的意思是:

    • 加载一个App类,命名空间为:appconfigApp
    • 类中有一个静态方法: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中的常量

    注意哦:

    1. 获取类以外的常量,需要在cont方法增加第二个参数:true
    2. 如果是通过define设置的常量是不受命名空间影响的,如果需要使用命名空间作为常量,需要在define中以命名空间设置常量名

    加载函数

    这个我在网上找了很多资料关于:PHP自动加载函数,但是至今为止网络上讨论的基本都是“PHP 自动加载类”;所以下面这个“自动加载函数”也算是本人的一点点小创新

    lv('appconfig')->func('app_list', 'contab'));

    这段代码的意思是:

    • 自动加载PHP文件:appconfigapp_list.php,
    • 调用文件中的函数:app_list()
    • 向函数传入参数:contab

    加载函数和加载类都需要注意:

    1. 每个文件尽量保证单独一个类或函数
    2. 文件名请保持和函数名一致,大小写也必须一致

    如果说一个文件中有多个类或函数,请先调用主类名或主函数名(和文件同名的类、函数)之后再调用同文件下的其他类、函数,如下:

    // 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类库在加载的时候会根据情况进行判断哦~

    上面提到了将存命名空间和路径的概念,有几个地方需注意:

    1. 不能重复添加相同名称的包,例如:有个包的名称是conf,那么就不能再以conf命名其他路径和命名空间了
    2. 不能删除、替换、修改包名称

    原因很简单:lv类库中所有的系统默认包不允许替换、删除和修改

    解决办法:

    1. 使用不同名的包名称命名
    2. 如果你命名的包太多了,可以使用inPack的方法来进行判断:
      lv('path')->inPack('file_path');

      返回true则已存在相同的包名称;如果没有提供参数,将返回所有包及包对应的命名空间和路径

    小提示:虽然不可以重复命名相同的包,但是可以将一个命名空间、路径命名成多个包名,例如:

    lv('path')->save('file_path');

    将命名空间、路径重复命名的时候,注意不可将命名空间作为路径的方式记录在包中,也不可以将路径作为命名空间记录在包中

    常见问题

    1.函数重名加载报错

    分两种情况:

    • 第一种情况.自己写的函数或第三方类库函数和lv重名

      解决方法:

      // 第一种方法,使用use来获取Node对象
      use lvloadNode;
      $node = new Node($pack);
      
      // 第二种方法,老老实实写命名空间
      use lvrequestHTTP;
      $http = new HTTP('http://levi.cg.am');
    • 第二种情况.自己写的函数和第三方类库函数冲突

      解决方法:

      1. 尽量不要写和第三方类库相同的函数名,请自行搜编码规范
      2. 使用命名空间解决

    2.lv加载包的方法,能和之前命名空间一起使用吗?

    完全可以,而且不用担心是否会重复加载的问题。

    use lvrequestHTTP;
    
    new HTTP();
    lv('http')->create();

    3.可以通过lv这样的方式来写继承类吗?

    不可以,继承类需要写上完整的命名空间地址

    class Http_muit extends lvrequestHTTP;
    
    // 或者通过use的方式
    use lvrequestHTTP;
    class Http_muit extends HTTP;

    4.lv调用包的方式加载PHP和composer的autoloader有什么区别?

    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.cg.am/archives/3690

    我意识到:

    1. 作为一个类库应当站在更容易使用的角度去考虑
    2. 类可以自己不断完善、优化,但是给到外部的最好是一个单纯的对象,这样才不会给使用的人造成一会需要调用类、一会调用对象这样繁琐的感念

    其实早前在设计这个类库的加载方法的时候,已经考虑到了直接调用对象,参见:lvloadL::loadObj();所以只要在这个类的基础上构建一个子类即可,于是衍生出了lvloadNode,这个类继承自:lvloadL

    namespace lvload
    {
        use lvfilePath;
        class Node extends L
        {
            // ...

    不过如果是以对象作为外部调用的话,仍旧未达到简化的目的,于是我在类库的根目录建立了一个函数文件:lv.php;代码很简单,以根节点命名空间返回Node对象;代码如下:

    function lv($package, $subname = '')
    {
        return new lvloadNode($package, $subname);
    }

    至此完毕,就这么简单。如果你感兴趣,不妨留言给我提出宝贵建议。结束前在送上一枚安装类库的方法(包含在线、离线下载安装)

    安装和更新类库,见这里:

    http://levi.cg.am/archives/3534#install

    您可能也喜欢:
    jQuery加载图片
    PHP类库:lv,像JS事件一样,监听你的PHP对象
    无阻塞脚本加载方案
    Lazy Load, 延迟加载图片的 jQuery 插件
    简单而强大的HTTP请求类,来自类库:lv
    无觅


沪ICP备19023445号-2号
友情链接