作者:best.lei

提笔写初体验总不知道从何提及,直接聊PHP中的函数、PHP网络技能、数据库操作、PHP模板等觉得又不是初体验。
末了还是决定从PHP的面向工具、PHP的魔术方法、PHP的反射、PHP中的非常和缺点这4个方面大略先容一下。

PHP面向工具的“形”与“本”

php用户体验手册PHP初体验 CSS

这里我们就不给面向工具下定义了,不过我们还是要说一下类和工具的。
类是工具的抽象组织,工具是类的详细存在。
接下来我们就拿PHP为例,来磋商一下工具的“形”与“本”的问题。

在PHP中,每个类的定义都因此关键字class开头,后面是类名和一对花括号,括号中包含类成员和方法的定义。
如下是一个大略类的定义:

当把类工具序列化输出时,可以看出类工具在存储时类似于数组的形式。
那么类工具与数组从实质上又有什么差异与联系呢?接下来从工具“本”来剖析一下PHP对工具的底层实现。
如下是PHP源码中对变量的定义:

通过上面的代码我们也对PHP如何存储工具有了初步的认识,那工具与数组又是什么关系呢?通过PHP的源码可得,zvalue_object构造中有一个HashTable的类型,它便是存储数组的。
PHP工具的构造体中不仅有HashTable(用于存储类工具特有的属性),而且还有工具所属类的入口等,如下是PHP工具的组成:

个中PHP源码中zend_class_entry构造体中存储的便是类的指针,该构造体中包含类常量、静态属性、标准方法、魔术方法、自定义方法等。
而属性数组存储的是类工具的属性。
接下来我们还是以如上的Person类为例,谈一谈工具与数组:

PHP中的魔术方法

可能细心的你在工具组成的那张图中看到了魔术方法,但是上一节中并没有对zend_class_entry中的内容做任何先容。
那么什么又是魔术方法呢?魔术方法便是以两个下划线“__”开头、具有一些分外浸染的方法。
实在如上的Person类中,我们不经意间就利用了魔术方法__construct(),这个魔术方法便是布局方法。
用于在创建类工具时对属性进行赋值。
接下来我们将先容几个常见的魔术方法让大家对魔术方法有个初步理解。

1.__sleep():该魔术方法是在实行serialize()方法完成前被调用,该方法可以用来关闭工具可能具有的任何数据库连接、加密须要序列化工具属性的值、指定仅序列化工具的某些属性等等。

2.__wakeup():该魔术方法是在实行unserialize()方法完成前被调用,该方法可以用来建立工具可能的数据库连接、解密序列化工具属性的值等等。

3.__construct():类的布局方法,类工具在创建时会首先调用该方法,因此该方法中可以做一些类工具创建前的初始化事情。

4.__destruct():类的析构方法,当类工具在销毁时会调用该方法。

5.__get()和__set()方法,这两个方法紧张是实现了可以在类的外部通过工具直接访问类的私有属性,还可以增加类中没有定义的属性,如给$person工具增加marriage属性,只需$person->marriage=\公众married\"大众即可。

6.__call()和__callStatic():当类工具调用的方法不存在而且类中定义了__call()方法时,则会自动调用类的__call(),当类调用静态方法不存在而且类中定义了方法__callStatic()时,则会自动调用__callStatic()方法。

7.__toString()方法,该方法紧张用于格式化打印一个工具,该方法的设计原型来源于Java。
只有类中实现了__toString()方法才可以通过echo直接打印该工具。

关于PHP的魔术方法我们就大略先容到这里,由上我们可以看出从布局方法上,PHP比较于还稍有欠缺,但PHP中有__set()和__get(),使得动态增加工具的属性字段变得更加方便,而对付Java来说要实现类似的效果,就不得不借助反射API或直接修正编译后字节码的办法实现了。
Java中有反射机制,那么PHP中呢?接下来让我们来看一看PHP中的反射实现。

PHP中的反射机制

反射,直不雅观的理解便是根据到达地找到出发地和来源。
比如给出一个工具就可以找到工具所属的类、拥有哪些方法。
反射可以在PHP运行状态中,扩展剖析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,这种动态获取信息以及动态调用工具方法的功能称为反射API。

class Person { public $name; private $age; private $sex; public static $information = \"大众I come from the earth\"大众; public function __construct($name=\"大众zhangsan\"大众, $age=23, $sex=\"大众male\"大众) { $this->sex = $sex; $this->age = $age; $this->name = $name; } public function __sleep() { // TODO: Implement __sleep() method return array(\公众name\"大众,\"大众age\"大众); //该方法指定仅序列化工具的name和age属性 } public function __wakeup() { // TODO: Implement __wakeup() method. var_dump($this->name); //打印输出工具的name属性的值 var_dump($this->sex); //打印输出工具的sex属性的值,由于sex没有被序列化,因此输出null } public function __destruct() { // TODO: Implement __destruct() method. echo \公众The object will be destructed\"大众; } public function __get($field) { // TODO: Implement __get() method. echo \"大众the get method is executed<br>\公众; return $this->$field; } public function __set($key, $value) { // TODO: Implement __set() method. echo \公众The set method is executed<br>\"大众; $this->$key = $value; } public function __call($name, $arguments) { // TODO: Implement __call() method. echo \"大众被调用方法的名称为:$name<br>\"大众; echo \"大众传入该方法的参数为:\"大众, var_dump($arguments),\"大众<br>\公众; } public static function __callStatic($name, $arguments) { // TODO: Implement __callStatic() method. echo \公众被调用方法的名称为:$name<br>\"大众; echo \"大众传入该方法的参数为:\"大众, var_dump($arguments),\"大众<br>\"大众; } public function __toString() { // TODO: Implement __toString() method. return \"大众My name is $this->name.<br> I am $this->age years old and I am a $this->sex.\公众; } public function sayHello($other){ echo \"大众My name is $this->name, nice to meet you $other\"大众; }}$person = new Person();$reflect = new ReflectionObject($person);$props = $reflect->getProperties(); //获取工具的属性列表(所有属性)foreach ($props as $prop){ //打印类中属性列表 echo $prop->getName(),\"大众\r\公众;}echo \公众<br>\"大众;$methods = $reflect->getMethods(); //获取类的方法列表foreach ($methods as $method){ echo $method->getName(),\"大众\r\"大众;}echo \"大众<br>\"大众;//返回工具属性的关联数组var_dump(get_object_vars($person)); echo \公众<br>\"大众;//返回类的公有属性的关联数组var_dump(get_class_vars(get_class($person))); echo \"大众<br>\"大众;//获取类的方法名组成的数组var_dump(get_class_methods(get_class($person)));/输出结果如下:name age sex information__construct __sleep __wakeup __destruct __get __set __call __callStatic __toString sayHelloarray(1) { [\公众name\"大众]=> string(8) \"大众zhangsan\"大众 }array(2) { [\公众name\"大众]=> NULL [\"大众information\"大众]=> string(21) \公众I come from the earth\公众 }array(10) { [0]=> string(11) \公众__construct\公众 [1]=> string(7) \公众__sleep\"大众 [2]=> string(8) \公众__wakeup\"大众 [3]=> string(10) \"大众__destruct\公众 [4]=> string(5) \"大众__get\"大众 [5]=> string(5) \公众__set\"大众 [6]=> string(6) \"大众__call\"大众 [7]=> string(12) \"大众__callStatic\"大众 [8]=> string(10) \公众__toString\公众 [9]=> string(8) \"大众sayHello\"大众 }/

如上代码中先容的是通过工具获取类的方法和属性字段,而反射不仅仅可以用于类和工具,还可以用于函数、扩展模块、非常等。
既然反射可以探知类的内部构造,那么就可以利用反射机制实现插件的功能,也可以利用反射机制实现动态代理。
接下来举个大略的例子看看如何通过反射机制实现动态代理。

如上的代码中真正实现sayHello()动作的是Person类中的sayHello()方法,而Dynamicproxy仅是一个代理类,个中并没有定义sayHello()方法,而是通过__call()方法动态调用类Person的sayHello()方法。
在DynamicProxy类中可以做sayHello()方法的前后拦截,并且可以动态的改变类中的方法和属性。
很多时候,善用反射可以保持代码的优雅和简洁,但反射也会毁坏类的封装性,由于反射可以使本不应该暴露的方法或属性被逼迫暴露了出来。

PHP中的非常和缺点

在措辞级别常日有许多缺点处理模式,但这些模式每每建立在约定俗称的根本上,也便是缺点都是可预知的。
不同的措辞对非常和缺点的定义也是不一样的,在PHP中,碰着任何自身缺点都会触发一个缺点,而不是抛出非常。
也便是说PHP一旦碰着非正常代码,常日都会触发缺点,而不是抛出非常。
因此如果想利用非常处理不可预见的问题,是办不到的。
比如,想在文件不存在或数据库无法建立连接时触发非常,是不可行的。
PHP会把这些作为缺点抛出,而不是作为非常捕获。
还是回到PHP的缺点处理上,PHP中的缺点级别大致分为以下几类:

最高级别的缺点是语法解析缺点 prase error。
该缺点属于语法检讨阶段缺点,这会导致PHP代码无法通过语法检讨。
次之的缺点是fetal error。
该类缺点会导致PHP流程终止,其后的代码无法连续实行。
warning是警告级别的缺点,在语法中涌现很不恰当的情形才会报此缺点,如参数不匹配、除数为0等。
这种缺点会导致不可预期的结果。
notice是关照级别的缺点,这种缺点是在如变量利用前未定义、数组索引是字符时没有加引号等情形。
最低级别的缺点是deprecated的缺点,表示不推举,不建议。
如在PHP5中利用ereg系列的正则匹配函数就会报此类缺点,该缺点是由于利用了不推举、过期的函数或语法造成的,不影响PHP正常流程。

接下来我们看一看针对上边先容的各个级别的缺点PHP是如何处理的。
PHP中供应了set_error_handler()函数来处理缺点,当然该函数也不是可以托管所有种类的缺点,如E_ERROR、E_PARSE、E_CORE_ERROR等缺点,这些缺点会以原始的办法显示。
当然也可以通过restore_error_handler()取消接管:

function DivisionError($errno, $errmsg, $errfile, $errline){ // 获取当前缺点韶光 $dt = date(\公众Y-m-d H:i:s (T)\"大众); $errortype = array ( E_ERROR => 'Error', E_WARNING => 'Warning', E_PARSE => 'Parsing Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Runtime Notice', E_RECOVERABLE_ERROR => 'Catchable Fatal Error' ); $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE); $err = \"大众缺点韶光\公众.$dt.\"大众<br>\公众; $err .= \"大众缺点等级\"大众.$errno .\"大众<br>\"大众; $err .= \公众缺点等级名称\公众 . $errortype[$errno] . \公众<br>\"大众; $err .= \"大众缺点\公众 . $errmsg . \"大众<br>\"大众; $err .= \"大众缺点发生所在文件\"大众 . $errfile . \"大众<br>\"大众; $err .= \"大众缺点所在行\公众 . $errline . \"大众<br>\"大众; echo $err;}set_error_handler(\公众DivisionError\"大众); //当抛出错误时,直接交给DivisionError处理function showError(){ $date = '2017-06-05'; if(ereg(\"大众([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})\"大众,$date,$regs)){ //deprecated级别缺点 echo $regs[1].$regs[2].$regs[3]; }else{ echo \"大众Invalid date format: $date\"大众; } if($value > 5){ //notice级别的缺点 echo \"大众It is amazing, the variable $value is not init\"大众,PHP_EOL; } $a = array('o'=>2,4,6,8); echo $a[o]; //notice级别的缺点 $sum = array_sum($a,3); echo fun(); //fetal error echo \"大众the code is after fetal error\公众; //该句不实行 //echo \公众prase error:\公众,$55;}showError();

如上这种“弯曲迂回”的处理办法也存在问题:必须依赖程序员自己来掌控对非常的处理,对付非常高发区、敏感区,如果处理不好就会涌现业务数据不一致的问题,但是优点便是可以得到程序运行的高下文信息,以进行针对性补救。

对付代码中存在的非常,须要认为的进行抛出,接下来我们通过自定义一个非常类来处理抛出的非常,

class DivisionException extends Exception{ //自定义非常处理类 public function __toString(){ //覆写父类的__toString(),规定输出格式 $errorMessage = '缺点发生于文件'.$this->getFile().'第'.$this->getLine().'行<br>' .'缺点缘故原由为'.$this->getMessage(); return $errorMessage; }}class Calculate { private $num1; private $num2; private $operater; public function __construct($num1, $num2, $operater) { $this->operater = $operater; $this->num1 = $num1; $this->num2 = $num2; } public function getResult() { try { if ($this->operater == \公众+\"大众) { return $this->num1 + $this->num2; } else if ($this->operater == \"大众-\公众) { return $this->num1 - $this->num2; } else if ($this->operater == \"大众\"大众) { return $this->num1 $this->num2; } else { if($this->num2 == 0){ //如果除数为0则抛出非常 throw new DivisionException(\"大众除数不能为0\公众); } return $this->num1 / $this->num2; } } catch (DivisionException $exception){ echo $exception; } }}$calculate = new Calculate(10,0,\"大众/\"大众);echo $calculate->getResult();/输出结果如下:缺点发生于文件/Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php第98行缺点缘故原由为除数不能为0/

初体验就为大家先容到这里吧,往后有机会在深入的去理解PHP函数、PHP与网络、PHP与数据库等等的内容吧。