php细嚼

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

php细嚼_第1张图片
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;
    这么一讲,再来个图,就很清楚了:

    php细嚼_第2张图片
    图丑理正

经验

  • 自动加载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
# helper.php
# helperTwo.php

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

php细嚼_第3张图片
输出图片

由此可见,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中也会有.此时输入则是:

php细嚼_第4张图片
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."
"; echo $errstr."
"; echo $errfile."
"; echo $errline."
"; 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

相对路径会有一个遍历目录的过程;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. 引入程序和代码当外部库用
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的界面,将界面上的所有内容粘贴到提示框内,然后确定,就会得知是和的版本,之后再下载即可

  2. 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细嚼)