php细嚼

作者: 某言 | 来源:发表于2019-01-11 19:08 被阅读0次

    语言只是工具,要的是思路,这里是一些常用又忽视的东西,后者有帮助的东西.

    php

    基础理解

    • 关于自身
      实际上php本身就是一门脚本语言,入门本该像刚刚入门python或者java一样,都是到官网下载对应版本的语言安装包,然后啪啪啪在键盘上敲一个hello world的程序,之后命令行打开,运行脚本,哗的一下,命令行就输出了"hello world"啦.
      但是,很多教程都把它当做是一门专门做web的语言,貌似就局限在web开发上了,一入门就安装个什么wamp或者upupw之类的集成环境,让初学者,甚至学了几年之后的人,也感觉不像门后台的语言,不知道它作为脚本的意义.我见过很多培训机构出来的学生,拿到一个需求,不好好分析,先他娘的来一个wamp+thinkphp的全套件先套上再说...不深入基础,认识不深刻,就多了很多网上所谓:php必亡的言论.
      当好好研究,真正当门脚本和后台语言来研究之后,就会发现,实际上,无论python,php还是java,都只是工具,一个能实现的,另外的几乎也能够实现,以前都是爬虫用python,谁说呢?后来我深入了解php之后,感觉php爬虫也很厉害的,packagist上也有很多库供我们使用,也有composer作为包管理工具,也可以用于服务器运维.谁说非要用python和shell?

    • php,httpserver,cgi的关系(论php集成环境各要素作用)
      本着代码中,什么都是接口的态度:
      php是脚本语言,本是不能够直接用于web应用的,只能够在命令行执行,是程序暴露的系统接口,输入是php的语法,输出是对系统的操作,只是比较高级,无法涉及到内存这些,如c便行;
      httpserver是一个处理http协议的请求的程序,能够接受,解析http的请求并且返回http协议的数据,像一个输入和输入都必须满足http协议的接口;
      cgi在我看来,也是一个接口程序,什么样的接口呢?是输入是httpserver已经输入的document(含有php语法的),输出是把php语法解析了的html或是其他纯纯的document;
      这么一讲,再来个图,就很清楚了:

      图丑理正

    经验

    • 自动加载class
      在很多php框架设计的时候,都用这种思路.所使用的函数spa_autoload_register(),虽然说命名空间可以随便的取,但是在这种情况下,还是和路径对应,才方面处理,但也不是一定的.
    # base.php
        namespace app;
        use libs\classOne;
        use libs\classTwo;
        //自动加载
        spl_autoload_register(function($className){
            $className = str_replace('\\',DIRECTORY_SEPARATOR,$className);
            echo $className; # 得到的是`libs\classOne`,命名空间的路径会被附带
            include($className.'.php');
        });
        $one = new  classOne();
        $two = new classTwo();
    
    # libs/classOne.php
        namespace libs;
        class classOne{
            function __construct(){
                echo "this is initial of fileOne";
                echo PHP_EOL;
            }
            function show(){
                echo "This is show function of classOne";
                echo PHP_EOL;
            }
        }
    
    # libs/classTwo.php
        namespace libs;
        class classTwo{
            function __construct(){
                echo "this is initial of fileOne";
                echo PHP_EOL;
            }
            function show(){
                echo "This is show function of classOne";
                echo PHP_EOL;
            }
        }
    
    • namespace
      由于其多级引入,方法,变量可能重复,即使同名的方法,需要区别.命名空间和文件的路径是有区分的,但是,感觉大部分都是和实际路径相关或者类似.
      要注意的是,其并不是路径,不等于文件所在的路径,而是要理解为:当很多class都引入到一个php文件中时候才来区分各个方法,才假以命名空间,仅仅是一个区分的标志
      如同在一个目录下的三个文件app.php,b.php,a.php如下:
    # app.php
    # 如果不用use也可以,但是调用就要写全名了
        use \app\utils as path; # 其中没有类,只有方法和变量,只可路径别名了;
        use \app\utils\ClassA as ClassAUse;# ClassA是类,可以别名;
        include("utils/a.php");
        include("utils/b.php");
        path\showB();
        \app\utils\showB();
        $obj = new ClassAUse();
        $obj->showA();
    
    # a.php
        namespace app\utils;
        class ClassA{
            function showA(){
                echo "this is show in a ";
                echo PHP_EOL;
            }
        }
    
    # b.php
        namespace app\utils;
        function showB(){
            echo "this is show in b ";
            echo PHP_EOL;
        }
    

    一般命名空间以入口文件所在目录为基点,按照目录来写命名空间,在spl_autoload_register()设计时候很有遍历

    • 常量constdefine
      constdefine,const是种结构,仅仅只可在class中使用,不可在if中使用,一般用于类中常量;define()是函数,除了class(而不是类方法中),一般用于全局的常量.
      如代码示例:
    define("NAME","fairy",true);
    if(true){
        define("AGE",24);
    }
    # const user = 'me' #打开注释报错
    class Test{
        const version = 1.2;
        # define("GOOD",'here in'); #打开注释报错
        function show(){
            define("GOOD",'here in');
            echo NAME;
            echo PHP_EOL;
            echo Test::version;
            echo PHP_EOL;
            echo GOOD;
            # if(true){ # 打开注释报错
            #   const variable = 'show';
            # }
        }
    }
    $test = new Test();
    $test->show();
    
    • 多级引入的问题
      做简单假设,比如有文件do.php,helper.php,helperTwo.php,在同一个目录下,且内容如下
    # do.php
    <?php
        require("./helper.php");
        showHello();
        showHelloTwo();
    
    # helper.php
    <?php
        require('./helperTwo.php');
        echo "hello,I am helper";
        echo PHP_EOL;
        function showHello(){
            echo "I am showHello of helper";
            echo PHP_EOL;
        }
    
    # helperTwo.php
    <?php
        echo "hello,I am helperTwo";
        echo PHP_EOL;
        function showHelloTwo(){
            echo "I am showHello of helperTwo";
            echo PHP_EOL;
        }
    

    时候,当我在命令行执行php do.php时候,不会报错,输出的是:

    输出图片

    由此可见,do.php引入helper.php,helper.php引入helperTwo.php,此时do.php也有了helperTwo.php的方法.由此可见,php的引入,实际可以理解为在哪里require/include,就相当于在哪里插入了这个文件里面的所有代码.还有一点可以证明:如果helper.php中规定一个和helperTwo.php中的showHelloTwo()方法时候,会报错说:

    报错说重复声明方法

    而在python的import,有点不同的情况,若有a.py,b.py,c.py在同一目录下,简单代码如下:

    # a.py
    import b
    b.show()
    print("I am base.py ")
    #b.showTwo() # 这里打开注释报错,不能够应用c.py中方法
    
    # b.py
    import c
    print("I am helper")
    helperTwo.showTwo()
    def show():
        print("I am show from helper's show()")
    
    # c.py
    print("I am helperTwo")
    def showTwo():
        print("I am show from helperTwo's showTwo()")
    

    a.py引入b.py,b.py引入c.py,此时,a.py中可以使用b.py中方法,b.py可以使用c.py中方法,但是a.py中不可以使用c.py中方法,而c.py中的执行了的部分print('...')则在a.py中也会有.此时输入则是:

    python a.py输出
    • 善于异常处理,保证系统不报错
      系统异常时候经常出现乱码这个是技术问题,要善于利用异常的处理,即使有异常,也不应该影响程序的正常工作,这方面做得不够很low的.每个地方的代码,都要独立考虑,剥离开联系考虑其是否会异常并且做好异常的处理.比如在很多mvc的框架中,model,controller,view各个板块,都要独立考虑异常的处理.另一方面,php的异常处理方法只能够处理些异常,而不能够处理核心的错误(语法,0作除数).
    # try{}catch(){}方法思路一览
    function checkNum($val){
        if($val<1){
            throw new Exception("Data can not less than 1");
        }
    }
    try {
        checkNum(0);
        echo "Your number is ok";
        echo PHP_EOL;
    } catch (Exception $e) {
        echo "error msg: ".$e->getMessage();
        echo PHP_EOL;
    }
    
    # 遇到error触发set_error_handler(),调用dealError函数进行处理
    set_error_handler('dealError');
    function dealError($errno,$errstr,$errfile,$errline){
        echo $errno."<br>";
        echo $errstr."<br>";
        echo $errfile."<br>";
        echo $errline."<br>";
        echo "this is dealing error";
    }
    # 主动触发set_error_handler函数
    trigger_error("self define error");
    

    常用语句效率

    • 静态方法和普通方法
      静态方法在程序开始后就会存在于内存,而非静态的,需要先实例化,之后才运行,多了一个步骤.
      测试代码如下:
    $times = 500000;
    $start = microtime(true);
    class Helper{
        public static function staticMethod(){
            $hello = "world";
            $good = "luck";
        }
        public function normalMethod(){
            $hello = "world";
            $good = "luck";
        }
    }
    for($i=0;$i<$times;$i++){
        // $good = new Helper();
        // $good->normalMethod();
        Helper::staticMethod();
    }
    $end = microtime(true);
    $delta = $end-$start;
    echo 'The code execute for '.$delta.' seconds';
    

    结果:如果for循环内的部分如上,那么确实静态方法要高效些.如果for改一下如下:

    $good = new Helper();
    for($i=0;$i<$times;$i++){
        // $good->normalMethod();
        Helper::staticMethod();
    }
    

    实例化的过程在for外面,如此两者几乎没有什么太大的差别.

    • include.//,includeinclude_once
    <?php
    
    $times = 1000;
    $start = microtime(true);
    
    #include_once('/helper.php');
    #include_once('/helperTwo.php');
    include('/helper.php');
    include('/helperTwo.php');
    #include('./helper.php');
    #include('./helperTwo.php');
    
    $end = microtime(true);
    $delta = $end-$start;
    echo 'The code execute for '.$delta.' seconds';
    

    相对路径会有一个遍历目录的过程;require_once()多了一个判断是否引入郭的过程.

    • ""'',issetstrlen
    //这的两个方法差别不是很大如果执行的次数较少,一般不容易遇到显示出差距的情况
    $times = 1000000;
    $start = microtime(true);
    
    for($i=0;$i<$times;$i++){
    
        # str的""和''
        $inner = "ok";
        // $str = "this is test string {$inner}"; 
        $str = 'this is test string'.$inner; //比起上面较为高效
        
        //isset 和 strlen
        $foo = "dddd";
        $flag = null;
        #if(strlen($foo)<5){
        if(isset($foo{5})){  //比起上面较为高效
            $flag = true;
        } 
    }
    $end = microtime(true);
    $delta = $end-$start;
    echo 'The code execute for '.$delta.' seconds';
    
    • gzcompressgzuncompress
    # 在blog时候就区别很大了,下面的语句,对于这个$str,没处理就存储的
    # 最后大小可达60M+,而经过压缩的,大小不到200k,在网络上传输的时候,
    # 差距可明显了
    $times = 2500000;
    $start = microtime(true);
    
    $origin = "abcdefghijklmnopqrstuvwxyz";
    $str = '';
    for($i=0;$i<$times;$i++){
        #gzcompress和gzuncompress
        $str .= $origin;
    }
    
    # 压缩后在存储
    $strDeal = gzcompress($str);
    file_put_contents('db.txt', $strDeal);
    $getStr = file_get_contents('db.txt');
    $originStr = gzcompress($getStr);
    
    # 直接存储
    # file_put_contents('db.txt',$str);
    # $originStr = file_get_contents('db.txt');
    
    $end = microtime(true);
    $delta = $end-$start;
    echo 'The code execute for '.$delta.' seconds';
    

    小工具

    • phar文件的使用
      phar类似于java里面的jar包,是一个功能的打包,从这个方面来说,也像webpack对javascript所做的一样,就是将多个引入的外部程序和入口程序打包,压缩在一起,方便于调用.
    • 使用:
    1. 在命令行直接运行
    php package.phar
    
    1. 引入程序和代码当外部库用
    <?php
        # 下面两行include不能并存,重复声明了如果并存就
        # 直接引入,包括打包在里面的方法也一并引入
        include('user.phar');
        # 仅仅引入打包于其中的一个库
        include("phar://user.phar/User.class.php");
        /* 
        # 相对路径写法
        include('../user.phar');
        include("phar://../user.phar/User.class.php");
        */
    
    
        # 无论存在那个include,都可执行
        $user = new User();
        $user->introduce();
    
    • 如何生成phar文件
      在使用phar的方法时候,需要修改php.ini中设定:
    phar.readonly = 0
    

    后重启服务器和命令行才行。

    $phar = new Phar('build.phar');
    // 添加project里面的所有文件到yunke.phar归档文件
    $phar->buildFromDirectory(dirname(__FILE__));
    $phar->compressFiles(Phar::GZ);
    //设置执行phar时的源文件的入口文件,第一个入口用于命令行,第二个入口用于浏览器访问,这里都设置为index.php
    $phar->setDefaultStub('index.php', 'index.php');
    

    如上假设是个网页应用,则仅仅适合单入口文件的打包,多入口,目前不太知道如何多个入口。

    • Composer
      实际和node的npm很像,专门提供了强大的外部php库和库的管理工具.基本的命令:
    # 初始化,生成一个composer.json文件
    composer init  
    # 更具composer.json和composer.lock文件,下载更新当前库
    composer install
    # 表明库版本的文件
    composer.lock
    # 安装某个库
    composer require libraryName
    # 创建项目
    composer create-project
    # 查看全局设定
    composer config -gl
    # 将镜像设定为中国的这个链接,比较快
    composer config -g repo.packagist comoser https://packagist.phpcomposer.com
    # 查看已存在的库
    composer show -i
    # 移除某个库
    composer remove libraryName
    

    凡是composer require安装的库,都会到vendor目录下,而vendor目录下有一个 文件:
    autoload.php
    如果想要运用vendor里面的所有库,只需要引入autoload.php即可使用下载的所有库了.比如最简单的日志模块monolog的使用:

    # 必须引入这个才能够使用vendor中的库,引入后相当于暴露了相应的方法
    include("vendor/autoload.php");
    use Monolog\Logger;
    use Monolog\Handler\StreamHandler;
    $log = new Logger('Kuroba');
    $log->pushHandler(new StreamHandler('helper/your.log', Logger::WARNING));
    // add records to the log
    $log->warning('There are some errors');
    $log->error('There are some mistakes');
    
    • Quick Server
    php -S localhost:8888
    
    • 运维
      php作为脚本,肯定是可以运维的,主要是两个函数:
    system("command string","stdout string");
    system("command string", [std , out , arr] , status[int])
    
    

    像那些通过web来登陆控制台的就是利用这样的方法了吧.

    • Xdebug
    1. 下载相关版本和平台的.dll文件放在php的拓展目录【注意,要选择对应xdebug的版本才行,否则无效】,检测的链接是:请点击,之后,在本地跳出phpinfo的界面,将界面上的所有内容粘贴到提示框内,然后确定,就会得知是和的版本,之后再下载即可,或者执行phpinfo();来查看php的版本信息来对应下载相关的xdebug的版本,如下图:
      php版本信息
      如上图几个地方标注,对应下载的xdebug的后缀应该:
      php_xdebug-{xdebugVersion}-7.2-vc15-nts-x86_64.dll
    1. php.ini所增加配置:
    # 注意我使用的是wamp集成环境,这个地方我的直接从文件夹打开php.ini加下列配置无效;
    #而且我从wamp进去的php.ini和从文件里面打开的不是一个,虽然可能不是共性的问题,但是还是值得记录
    # 遇到问题时候有个参照
    [xdebug]
    zend_extension ="D:/path/php5.6.25/ext/php_xdebug-2.2.5-5.6-vc11-x86_64.dll"
    xdebug.auto_trace = On
    xdebug.show_exception_trace = On
    xdebug.remote_autostart = On
    xdebug.remote_enable = On
    xdebug.collect_vars = On
    xdebug.collect_return = On
    xdebug.collect_params = On
    xdebug.trace_output_dir="D:/path/php5.6.25/xDebugLog"
    xdebug.profiler_output_dir="D:/path/php5.6.25/xDebugLog"
    xdebug.profiler_enable=On
    xdebug.remote_host=localhost
    xdebug.remote_port=9000
    xdebug.remote_handler=dbgp
    
    1. 使用eclipese for phpvscode或者配置sublime即可以断点试调

    相关文章

      网友评论

          本文标题:php细嚼

          本文链接:https://www.haomeiwen.com/subject/ouqzrqtx.html