语言只是工具,要的是思路,这里是一些常用又忽视的东西,后者有帮助的东西.
基础理解
关于自身
实际上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()设计时候很有遍历
- 常量
const
和define
const
和define
,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
时候,不会报错,输出的是:
由此可见,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
中也会有.此时输入则是:
- 善于异常处理,保证系统不报错
系统异常时候经常出现乱码这个是技术问题,要善于利用异常的处理,即使有异常,也不应该影响程序的正常工作
,这方面做得不够很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
的./
和/
,include
和include_once
相对路径会有一个遍历目录的过程;require_once()
多了一个判断是否引入郭的过程.
""
和''
,isset
和strlen
//这的两个方法差别不是很大如果执行的次数较少,一般不容易遇到显示出差距的情况
$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';
gzcompress
和gzuncompress
# 在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所做的一样,就是将多个引入的外部程序和入口程序打包,压缩在一起,方便于调用. - 使用:
- 在命令行直接运行
php package.phar
- 引入程序和代码当外部库用
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
下载相关版本和平台的
.dll
文件放在php的拓展目录【注意,要选择对应xdebug的版本才行,否则无效】,检测的链接是:请点击,之后,在本地跳出phpinfo的界面,将界面上的所有内容粘贴到提示框内,然后确定,就会得知是和的版本,之后再下载即可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
- 使用
eclipese for php
、vscode
或者配置sublime
即可以断点试调