最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 用PHP从零开始写一个编译器(五)

    正文概述 转载于:掘金(祁宏)   2021-05-17   609

    前面的链接:

    php从零开始写一个编译器(一)

    用PHP从零开始写一个编译器(二)

    用PHP从零开始写一个编译器(三)

    用PHP从零开始写一个编译器(四)

    前面我们把整个编译器的算法讲解完成了。Parser类编译完成以后是一个PHP程序可用的数组。但是,为了对程序员友好,我们最后把数据封装到了Query类中。为什么要这么做呢,这是因为,对于完整的查询或者是查询语句片断,PHP程序使用时是要区别对待的。其实,任何一个开源,如果你对程序员不友好,那就是死路一条。这也就是为什么Laravel框架能在短时间内成为世界第一,因为,它真的是好用。

    其实,Query类,也并没有做什么,它就是实现了QueryInterface这个接口。

    declare(strict_types=1);
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\RqlParser;
    
    /**
     * Interface QueryInterface
     *
     * @package ByteFerry\RqlParser
     *
     * @property array $container
     */
    interface QueryInterface
    {
    
        /**
         * @return QueryInterface
         */
        public static function of();
    
        /**
         * @param array $query
         *
         * @return \ByteFerry\RqlParser\QueryInterface
         */
        public function from($query);
    
        /**
         * @param $name
         *
         * @return mixed|null
         */
        public function __get($name);
    
    
        /**
         * @param $name
         *
         * @return bool
         */
        public function __isset($name);
    
    
        /**
         * @return mixed
         */
        public function toArray();
    }
    
    

    而实现这个接口的有两个类,一个叫Fragment,它是RQL查询片段。

    declare(strict_types=1);
    
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\RqlParser;
    
    use ByteFerry\RqlParser\Abstracts\BaseObject;
    
    /**
     * Class Fragment
     *
     * @package ByteFerry\RqlParser
     */
    class Fragment extends BaseObject implements QueryInterface
    {
    
        /**
         * @var array
         */
        protected $container = [];
    
    
        /**
         * @param array $query
         *
         * @return \ByteFerry\RqlParser\Fragment
         */
        public function from($query)
        {
            $this->container = $query;
            return $this;
        }
    
        /**
         * @param $name
         *
         * @return mixed|null
         */
        public function __get($name)
        {
            if (isset($this->container[$name])) {
                return $this->container[$name];
            }
            return null;
        }
    
        /**
         * @param $name
         *
         * @return bool
         */
        public function __isset($name)
        {
            return isset($this->container[$name]);
        }
    
        /**
         * @return array|mixed
         */
        public function toArray(){
            return $this->container;
        }
    }
    
    

    另一个则是Query,Query对象提供了

    declare(strict_types=1);
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\RqlParser;
    
    use ByteFerry\RqlParser\Abstracts\BaseObject;
    
    /**
     * Class Query
     *
     * @package ByteFerry\RqlParser
     */
    class Query extends BaseObject implements QueryInterface
    {
    
        /**
         * @var array
         */
        protected $container = [];
    
    
        /**
         * @param array $query
         *
         * @return \ByteFerry\RqlParser\Query
         */
        public function from($query)
        {
            $this->container = $query;
            return $this;
        }
    
        /**
         * @param $name
         *
         * @return mixed|null
         */
        public function __get($name){
            if(isset($this->container[$name])){
                return $this->container[$name];
            }
            return null;
        }
    
        /**
         * @param $name
         *
         * @return bool
         */
        public function __isset($name){
            return isset($this->container[$name]);
        }
    
        /**
         * @return array|mixed
         */
        public function toArray(){
            return $this->container;
        }
    
        /**
         * @return mixed|null
         */
        public function  getOperator(){
            return $this->container['operator']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function  getQueryType(){
            return $this->container['query_type']??null;
        }
    
        /**
         * @return string
         */
        public function getResourceName(){
            return $this->container['resource']??null;
        }
    
        /**
         * @return array
         */
        public function getColumns(){
            return $this->container['columns']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getColumnsOperator(){
            return $this->container['columns_operator']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getGroupBy(){
            return $this->container['group_by']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getFilter(){
            return $this->container['filter'][0]??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getParameters(){
            return $this->container['paramaters']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getSearch(){
            return $this->container['search']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getSort(){
            return $this->container['sort']??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getHaving(){
            return $this->container['having'][0]??null;
        }
    
        /**
         * @return mixed|null
         */
        public function getLimit(){
            return $this->container['limit']??null;
        }
    
    

    项目当中,剩下的两个类,那就是BaseObject 和 ParseException,无需讲解。接下来,就是单元测试了。

    为什么要单元测试?假如你写的组件,那是交给天下人用的。如果未经测试,谁愿意做你的小白鼠?使用单元测试,我可以用composer下载之后,可以通过PhpUnit命令行验证一下。为什么要验证?因为,php版本,关联组件版本可能不一样。就象一些公司,说明,有新版本了,于是,尽快升级,但是,一升级,马上就崩了。这种情况相当常见,毕境,一旦有关联组件,肯定就有兼容问题,比如,thinkphp5.0就不兼容现在的redis,redis把函数名改了,del改成delete了。这还是小事。像thinkphp5.0与thinkphp5.1直接未在composer中设置版本冲突,所以,你在用5.0时,一不小心升级了,于是崩了。再如,thinkphp5系列均不带PhpUnit,所以,当我安装了组件之后,我无法验证可不可运行。只有在开发了,代码出来以后,才开始报错,这就浪费了大量的时间。所以,一般我composer安装后,喜欢用PhpUnit验证一下之后再开发,如果不行,则换版本,或换组件。

    由此来看,你把你的想法实现了,交给大家来用,这是开源精神,值得称赞,但是,实际上,github中大量的开源并不是按此规范做的。所以,那些开源基本无人问津。

    那么,该怎么测呢?首先我们要安装PhpUnit 和 orchestra/testbench,为什么要安装orchestra/testbench呢?这是为了能和travisCI这个免费的持续集成工具集成方便。另外,写代码也会方便很多。

    关于这两个组件的使用,不多讲了,大家还是直接看文档。基本步骤是,配置phpuint.xml,用以告诉它,怎么测。然后,继承一个TestCase,废话少说,上代码:

    declare(strict_types=1);
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\Tests;
    
    use Orchestra\Testbench\TestCase as Orchestra;
    
    /**
     * Class TestCase
     *
     * @package ByteFerry\tests
     */
    class TestCase extends Orchestra
    {
        /**
         * The base URL to use while testing the application.
         *
         * @var string
         */
        protected $baseUrl = 'http://localhost';
    
        public function setUp(): void
        {
    //        touch('./tests.sqlite');
    
            parent::setUp();
        }
    
        public function tearDown(): void
        {
    //        unlink('./tests.sqlite');
        }
    
        /**
         * Define environment setup.
         *
         * @param \Illuminate\Foundation\Application $app
         *
         * @return void
         */
        protected function getEnvironmentSetUp($app)
        {
            // Setup default database to use sqlite :memory:
            $app['config']->set('database.default', 'testbench');
            $app['config']->set('database.connections.testbench', [
                'driver' => 'sqlite',
                'database' => ':memory:',
                'prefix' => '',
            ]);
        }
    }
    

    这个类的代码基本都是默认的。接下来,要写相关测试了。 首先,我们要测RQL的所有关键字。

    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\Tests\Unit;
    
    use ByteFerry\Tests\TestCase;
    use  ByteFerry\RqlParser\Parser;
    /**
     * Class KeyTest
     *
     * @package ByteFerry\tests\Unit
     */
    final class KeyTest extends TestCase
    {
        protected $test_keys = [
            'aggr' =>['aggr(id,sum(amount))','{"columns":["id","sum(amount)"],"columns_operator":"aggr","group_by":["id"]}'],
            'aggregate' =>['aggregate(id,sum(amount))','{"columns":["id","sum(amount)"],"columns_operator":"aggregate","group_by":["id"]}'],
            'and' =>['and(gt(age,20),lt(id,300),is(name,null()))','"( age > 20 )and( id < 300 )and( name is null )"'],
            'arr' =>['in(id,(1,34,2,4))','" id in  (1, 34, 2, 4)  "'],
            'avg' =>['aggr(id,avg(amount))', '{"columns":["id","avg(amount)"],"columns_operator":"aggr","group_by":["id"]}'],
            'between' =>['between(age,18,55)','" age BETWEEN 18 and 55 "'],
            'cols' =>['cols(id,age)','{"columns":["id","age"],"columns_operator":"cols","group_by":[]}'],
            'columns' =>['columns(id,age,name)','{"columns":["id","age","name"],"columns_operator":"columns","group_by":[]}'],
            'data' =>['data(id:17,score:33)','{"data":{"id":"17","score":"33"}}'],
            'empty' => ['is(name,empty())','" name is \"\" "'],
            'eq' =>['eq(age,17)','" age = 17 "'],
            'except' =>['except(password)','{"columns":["password"],"columns_operator":"except","group_by":[]}'],
            'false' => ['filter(false())','{"filter":[0],"paramaters":[]}'],
            'filter' =>['filter(eq(age,11))','{"filter":[" age = 11 "],"paramaters":{"age":"11"}}'],
            'ge' =>['ge(age,35)','" age >= 35 "'],
            'gt' =>['gt(age,15)','" age > 15 "'],
            'having' =>['having(ge(sum(amount),100))','{"having":[" sum(amount) >= 100 "],"paramaters":{"sum(amount)":"100"}}'],
            'in' =>['in(id,(1,3,5))','" id in  (1, 3, 5)  "'],
            'is' => ['is(name,null())','" name is null "'],
            'le' =>['le(age,3)','" age <= 3 "'],
            'like' =>['like(name,"ad%")','" name like \"ad%\" "'],
            'limit' =>['limit(1,20)','{"limit":["1","20"]}'],
            'lt' =>['lt(age,15)','" age < 15 "'],
            'max' =>['aggr(id,max(amount))','{"columns":["id","max(amount)"],"columns_operator":"aggr","group_by":["id"]}'],
            'mean' =>['aggr(id,mean(amount))','{"columns":["id","avg(amount)"],"columns_operator":"aggr","group_by":["id"]}'],
            'min' =>['aggr(id,min(amount))','{"columns":["id","min(amount)"],"columns_operator":"aggr","group_by":["id"]}'],
            'ne' =>['ne(age,15)','" age <> 15 "'],
            'nin' =>['nin(age,(15,25,35))','" age not in  (15, 25, 35)  "'],
            'not' =>['is(name,not(null()))','" name is not null "'],
            'null' =>['is(name,not(null()))','" name is not null "'],
            'only' =>['only(id,name,age,email)','{"columns":["id","name","age","email"],"columns_operator":"only","group_by":[]}'],
            'or' =>['or(eq(age,18),eq(gender,1))','"( age = 18 )or( gender = 1 )"'],
            'out' =>['out(id,(1,3,5))','" id not in  (1, 3, 5)  "'],
            'search' =>['search("jh%")','{"search":"jh%"}'],
            'select' =>['select(id,name,age)','{"columns":["id","name","age"],"columns_operator":"select","group_by":[]}'],
            'sort' =>['sort(+age,-name)','{"sort":[["age","ASC"],["name","DESC"]]}'],
            'sum' =>['aggr(id,sum(amount))','{"columns":["id","sum(amount)"],"columns_operator":"aggr","group_by":["id"]}'],
            'true' => ['eq(deleted,true())','" deleted = 1 "'],
            'values' =>['values(name)','{"columns":["name"],"columns_operator":"values","group_by":[]}'],
        ];
    
        /** @test */
        public function testKeys(){
            foreach($this->test_keys as $key => $item){
                [$test_str,$compare] = $item;
                $result = Parser::parse($test_str,true);
                $this->assertEquals($compare,json_encode($result[0]->toArray()));
            }
        }
    }
    

    这里,我们使用了一个偷懒的办法,那就是,我们把编译的结果用json_encode转换为字符串,与我们相要的字符串比对,如果一致,则OK。(这样做,是让代码行数变少)

    第二步,我们要测所有的完整的查询

    declare(strict_types=1);
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\Tests\Unit;
    
    use ByteFerry\Tests\TestCase;
    
    use ByteFerry\RqlParser\Parser;
    
    /**
     * Class SimpleQueryTest
     *
     * @package ByteFerry\tests\Unit
     */
    final class SimpleQueryTest extends TestCase
    {
        public $query_keys = [
            'all' =>['all(user,only(id,age))', '{"resource":"user","columns":["id","age"],"columns_operator":"only","group_by":[],"operator":"all","query_type":"Q_READ"}'],
            'any' =>['any(user,cols(id,name,age))','{"resource":"user","columns":["id","name","age"],"columns_operator":"cols","group_by":[],"operator":"any","query_type":"Q_READ"}'],
            'count' =>['count(user,cols(id))','{"resource":"user","columns":["id"],"columns_operator":"cols","group_by":[],"operator":"count","query_type":"Q_READ"}'],
            'create' =>['create(user,data(name:jhon,age:17))','{"resource":"user","data":{"name":"jhon","age":"17"},"operator":"create","query_type":"Q_WRITE"}'],
            'decrement' =>['decrement(Article,cols(read_count))','{"resource":"Article","columns":["read_count"],"columns_operator":"cols","group_by":[],"operator":"decrement","query_type":"Q_WRITE"}'],
            'delete' =>['delete(User,filter(eq(id,23)))','{"resource":"User","filter":[" id = 23 "],"paramaters":{"id":"23"},"operator":"delete","query_type":"Q_WRITE"}'],
            'distinct' =>['distinct(User_score,filter(gt(score,95)))','{"columns":["User_score",{"filter":[" score > 95 "],"paramaters":{"score":"95"}}],"columns_operator":"distinct","group_by":[]}'],
            'exists' =>['exists(user,filter(eq(mobile,1111)))','{"resource":"user","filter":[" mobile = 1111 "],"paramaters":{"mobile":"1111"},"operator":"exists","query_type":"Q_READ"}'],
            'first' =>['first(user,filter(gt(age,10)))','{"resource":"user","filter":[" age > 10 "],"paramaters":{"age":"10"},"operator":"first","query_type":"Q_READ"}'],
            'increment' =>['increment(Article,cols(read_count))','{"resource":"Article","columns":["read_count"],"columns_operator":"cols","group_by":[],"operator":"increment","query_type":"Q_WRITE"}'],
            'minus' =>['minus(Article,cols(read_count))','{"resource":"Article","columns":["read_count"],"columns_operator":"cols","group_by":[],"operator":"decrement","query_type":null}'],
            'one' =>['one(user,filter(eq(id,3)),search(ada))','{"resource":"user","filter":[" id = 3 "],"paramaters":{"id":"3"},"search":"ada%","operator":"one","query_type":"Q_READ"}'],
            'plus' =>['plus(Article,cols(read_count))','{"resource":"Article","columns":["read_count"],"columns_operator":"cols","group_by":[],"operator":"increment","query_type":null}'],
            'update' =>['update(user,data(age:18),filter(eq(id,3)))','{"resource":"user","data":{"age":"18"},"filter":[" id = 3 "],"paramaters":{"id":"3"},"operator":"update","query_type":"Q_WRITE"}']
        ];
    
        /** @test */
        public function testQuery(){
            foreach($this->query_keys as $key => $item){
                [$test_str,$compare] = $item;
                $result = Parser::parse($test_str);
                $this->assertEquals($compare,json_encode($result[0]->toArray()));
            }
        }
    
    }
    

    第三步,我们要测QueryInterface这个接口类

    declare(strict_types=1);
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\Tests\Unit;
    
    use ByteFerry\Tests\TestCase;
    use  ByteFerry\RqlParser\Parser;
    use  ByteFerry\RqlParser\Fragment;
    
    /**
     * Class QueryInterfaceTest
     *
     * @package ByteFerry\Tests\Unit
     */
    final class QueryInterfaceTest extends TestCase
    {
        /** @test */
        public function testQueryInterface(){
            $rql_str= 'all(User,aggr(id,name,age,gender,address,avg(age)),filter(is(created_at, null()), search(Jhon),sort(-id,+age),having(gt(sum(amount),0)),limit(0,20)))'; //,    //,
    
            $result = Parser::parse($rql_str);
            /** @var \ByteFerry\RqlParser\Query $query */
            $query = $result[0];
            $this->assertEquals($query->getResourceName(),'User');
            $this->assertEquals($query->getColumns(),[0 => 'id',
                1 => 'name',
                2 => 'age',
                3 => 'gender',
                4 => 'address',
                5 => 'avg(age)']);
            $this->assertEquals($query->getColumnsOperator(),'aggr');
            $this->assertEquals($query->getGroupBy(),[
                0 => 'id',
                1 => 'name',
                2 => 'age',
                3 => 'gender',
                4 => 'address']);
            $this->assertEquals($query->getFilter(),' created_at is null ');
            $this->assertEquals($query->getParameters(),[
                'created_at' => NULL,
                'sum(amount)' => '0'
            ]);
    
            $this->assertEquals($query->getSearch(),'Jhon%');
            $this->assertEquals($query->getSort(),[
                [   0 => 'id',
                    1 => 'DESC'
                    ],
                [
                    0 => 'age',
                    1 => 'ASC',
                ]
            ]);
            $this->assertEquals($query->getHaving(),' sum(amount) > 0 ');
            $this->assertEquals($query->getLimit(),['0','20']);
            $this->assertEquals($query->getOperator(),'all');
            $this->assertEquals($query->getQueryType(),'Q_READ');
            $this->assertEquals($query->query_type,'Q_READ');
            $this->assertEquals($query->operator,'all');
            $this->assertTrue(isset($query->operator));
            $this->assertEquals($query->name,null);
        }
    
        /** @test */
        public function testFragment(){
            $rql_str= 'aggregate(id,sum(amount))'; //,    //,
    
            $result = Parser::parse($rql_str,true);
            /** @var \ByteFerry\RqlParser\Fragment $query */
            $query = $result[0];
            $this->assertTrue($query instanceof Fragment);
            $this->assertEquals($query->columns,["id","sum(amount)"]);
            $this->assertEquals($query->group_by,["id"]);
            $this->assertEquals($query->columns_operator,'aggregate');
            $this->assertFalse(isset($query->operator));
            $this->assertEquals($query->operator,null);
        }
    }
    

    最后,我们还要进行异常测试。为什么呢?这是因为,出错时,你要保证抛出异常给程序员看到。同时,也只有经过异常测试,才能保证代码测试的覆盖率。

    declare(strict_types=1);
    /*
     * This file is part of the ByteFerry/Rql-Parser package.
     *
     * (c) BardoQi <67158925@qq.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    namespace ByteFerry\Tests\Unit;
    
    use ByteFerry\RqlParser\Fragment;
    use ByteFerry\Tests\TestCase;
    use  ByteFerry\RqlParser\Parser;
    use  ByteFerry\RqlParser\Exceptions\ParseException;
    /**
     * Class ExceptionTest
     *
     * @package ByteFerry\Tests\Unit
     */
    final class ExceptionTest extends TestCase
    {
        /** @test */
        public function testExceptions(){
            $rql_str= 'aggregate(id,,sum(amount))'; //,    //,
            try{
            $result = Parser::parse($rql_str,true);
            }catch(\Exception $e){
                $this->assertTrue($e instanceof ParseException);
            }
    
            $rql_str= 'aggregate(id,sum(amount)'; //,    //,
            try{
                $result = Parser::parse($rql_str,true);
            }catch(\Exception $e){
                $this->assertTrue($e instanceof ParseException);
            }
    
        }
    }
    

    最后,我们要集成到TraviCI , 并且,用CodeCov检查代码测试覆盖率。 如何配,这里,我们不是单独运行,因为,本组件是无任何依赖项的,我们选择集成到Laravel,为什么不选择thinkPHP?原因相当简单,它那里连phpUnit都没有,最后出错,用户认为我错了,岂不坏了我的名声?

    其实, 这些东西简单的,官方网站都有详细的文档,并且是免费的。 TraviCI配置tavis.yml如下:(是基于yml的配置)

    language: php
    
    php:
      - 7.2
    
    env:
      - LARAVEL_VERSION=5.8.*
      - LARAVEL_VERSION=6.*
      - LARAVEL_VERSION=7.*
      - LARAVEL_VERSION=8.*
    
    before_script:
      - travis_retry composer self-update
      - travis_retry composer install --prefer-source --no-interaction
      - if [ "$LARAVEL_VERSION" != "" ]; then composer require --dev "laravel/laravel:${LARAVEL_VERSION}" --no-update; fi;
      - composer dump-autoload
    
    script:
      - ./vendor/bin/phpunit --coverage-clover coverage.xml
    
    after_success:
      - bash <(curl -s https://codecov.io/bash)
    
    notifications:
    email:
      recipients:
        - bardoqi@gmail.com
      on_success: always
      on_failure: always
    
    

    测试做完了,集成完成,你可以将相关链接图标放到你的readme.md中。如下

    用PHP从零开始写一个编译器(五)

    (说明,上面是截图,这里无法正常显示动态的SHELD,只能上截图了)

    这些链接告诉用户,你的代码是经过测试的,这其中,

    第一个是 TravisCI 编译通过的标识

    第二个是SytleCI(代码格式规范)通过的标志,

    第三个,最重要的,就是测试代码覆盖率,总共覆盖了多少。我们可是99%

    后面三个则是packagist.org版本与下载量,以及你的授权。这些主要是让人一眼可以看出是否可以用。

    到此,我们的PHP代码编译器开发算是完成了。最后,别忘了,能够提供详细的文档,我们提供了中英文两个版本的readme,同时还提供了在线的RQL文档。因为,我们修改了一些RQL的关键字。此外,大家可以看出,此代码相当易用,因为,就是一个静态函数。为什么要这么做?越简单,大家用得越爽呀。另一方面,毕境是每个版本,目前不清楚客户会有什么配置项之类的。当然从简。

    大家可以到

    github.com/byteferry/r…

    国内链接: gitee.com/bulo/rql-pa…

    下载完整的原码。如果你觉得有用,别忘了star,既是自己收藏,也是一份鼓励。

    那么,这个组件的效率如何?我们在内存仅8G, i7CPU的笔记本环境,测试下来,最长的RQL查询的编译一般在2毫秒左右。看起来,这里有10多个文件,但是,对一个懂得设计模式的人来说,每一个文件最多300行,那代码阅读是相当开心的。换个初学者,一样可以一个文件,3000行一个函数,也能完成,但未来就惨了。

    所以,一些人认为,代码越多,程序越慢,实际是不对的。代码快与慢,关键是看数据结构与程序结构,不是代码多与少的问题。比如,ElasticSearch代码远比一般全文搜索的代码多得多了,为什么那么快?

    同时,我们也可以看到,我们代码中,没有一个函数超过30行的。

    那么,有人要问,你这是不是重造轮子?实际不是,因为,第一,目前任何一个PHP的RQL Parser均没有理想的程序结构。第二,更没有支持写查询的。第三,有些效率也不是很高。

    rql-parser 是我们的第一步计划,我们是根据组件化的原理,下一步要写ApiBirdge,用来真正增强RestAPi的调用。所以,这个组件中的RQL与现在RQL标准的草案还是有些区别的。

    自然,作者水平有限,也希望高手一些参与。RQL作为一项新技术,我相信未来肯定会被广泛运用到基础应用架构以及ORM等领域。作为个人,做开源的力量是有限的,所以,也希望感兴趣的企业共同参与。(全文完)


    起源地下载网 » 用PHP从零开始写一个编译器(五)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元