php面向对象__call处理错误调用技巧
在提到__call之前,先来看一个实例的测试结果,以便更好地去了解__call方法的作用。上代码:
在调用对象中不存在的方法时就会出现系统报错,然后程序退出不能继续执行。如果在类中添加一个“魔术”方法__call(),则调用对象中不存在的方法时就会自动调用该方法,并且程序可以继续向下执行。可以通过在__call()方法中的设置,提示用户调用的方法及需要的参数列表内容不存在。__call()方法需要两个参数,第一个参数是调用不存在的方法时,接受这个不存在的方法的方法名,并将这个不存在的方法中,使用的参数列表形成数组传给__call()方法中的第二个参数。
代码
代码如下 | 复制代码 |
<?php //这是一个测试的类,里面没有属性和方法
$test=new Test();
$test->demo("one", "two", "three");
echo "this is a test<br>";
|
运行结果:Fatal error: Call to undefined method Test::demo()
我们知道,程序的运行结果抛出了错误提示,在运行的过程中抛出错误后就已经中断了,以致”$Person->say();“这个正确的方法也不能再继续运行。看一下上面的代码就知道,Person类并没有代码错误,错就错在实例化Person类的过程中调用了Person类中并不存在的方法,如run()和eat()。
在程序的运行中,出现如上抛出的的错误是致命性的,整个程序将崩溃。为了处理这种错误的同时让程序继续执行,我们可以在类中添加一个魔术方法__call,来调用对象中不存在的方法时自动调用该方法,并且使程序可以继续向下执行。
下面将在上面的代码的基础上多添加一个__call方法并调试,代码如下:
代码
代码如下 | 复制代码 |
<?php //这是一个测试的类,里面没有属性和方法 //调用不存的方法时自动调用的方法,第一个参数为方法名,第二个参数是数组参数 function __call($function_name, $args) print "你所调用的函数:$function_name(参数:"; print_r($args); echo "不存在!<br>n"; } }
$test=new Test();
$test->demo("one", "two", "three");
echo "this is a test<br>"; ?> 运行结果: 你所调用的函数:run(参数:Array ( [0] => teacher ) )不存在! 你所调用的函数:eat(参数:Array ( [0] => child [1] => apple ) )不存在! Hello, wblog! |
这次程序的运行结果不再抛出致命性错误,在调用不存在的方法时自动调用了__call方法捕捉处理不存在的方法并提示给用户,而调用存在的方法时程序正常执行。
总结:在类中添加一个魔术方法__call,在调用对象中不存在的方法时就会自动调用该方法,并且程序可以继续向下执行。
我们前面说过在类里面声明“--”开始的方法名的方法(PHP给我们提供的),都是在某一时刻不同情况下自动调用执行的方法,“__toString()”方法也是一样自动被调用的,是在直接输出对象引用时自动调用的, 前面我们讲过对象引用是一个指针,比如说:“$p=new Person()“中,$p就是一个引用,我们不能使用echo 直接输出$p, 这样会输出”Catchable fatal error: Object of class Person could not be converted to string“这样的错误,如果你在类里面定义了“__toString()”方法,在直接输出对象引用的时候,就不会产生错误,而是自动调用了”__toString()”方法, 输出“__toString()”方法中返回的字符,所以“__toString()”方法一定要有个返回值(return 语句).
代码
代码如下 | 复制代码 |
<?php public function __construct($foo) { $class = new TestClass('Hello'); //直接输出对象 |
似曾相识,在php面向对象编程之魔术方法__set,曾经介绍了什么是魔术方法,这一章又介绍一个魔术方法__tostring()。
__toString()是快速获取对象的字符串信息的便捷方式,似乎魔术方法都有一个“自动“的特性,如自动获取,自动打印等,__toString()也不例外,它是在直接输出对象引用时自动调用的方法。
__toString()的作用
当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。
下面我们来看一个__toString()的实例
代码如下 | 复制代码 |
<?php 02 class Person{ 03 private $name = ""; 04 function __construct($name = ""){ 05 06 $this->name = $name; 07 } 08 function say(){ 09 10 echo "Hello,".$this->name."!<br/>"; 11 } 12 function __tostring(){//在类中定义一个__toString方法 13 return "Hello,".$this->name."!<br/>"; 14 } 15 } 16 $WBlog = new Person('WBlog'); 17 echo $WBlog;//直接输出对象引用则自动调用了对象中的__toString()方法 18 $WBlog->say();//试比较一下和上面的自动调用有什么不同 19 ?>
Hello,WBlog! Hello,WBlog! |
如果不定义“__tostring()”方法会怎么样呢?例如在上面代码的基础上,把“ __tostring()”方法屏蔽掉,再看一下程序输出结果:
Catchable fatal error: Object of class Person could not be converted to string
由此可知如果在类中没有定义“__tostring()”方法,则直接输出以象的引用时就会产生误法错误,另外__tostring()方法体中需要有一个返回值。
作用:当在程序中需要实例化一个类,刚好这这类又不是在本文件中,则需要用包含函数将外部文件包含进来。但是,当要用的外部类很多后,就会发现用包含函数会显得十分的繁琐,这是就可以用__autoload()全局函数自动加载类。
当在index.php中要使用前面三个类时,就需要写三个例如include("name.class.php") 这样的方法,效率会十分低,但如果使用了__autoload()函数就不用这样麻烦了,只需要写这样一个函数方法就可以了:
在 PHP 5 中,不再需要这样了。可以定义一个 __autoload() 函数,它会在试图使用尚未被定义的类时自动调用。通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
在下例中将通过实例来说明 __autoload() 是如何使用的。
首先定义一个类ClassA,文件名为ClassA.class.php
代码如下 | 复制代码 |
class ClassA{ public function funa(){ echo "classA loaded successfully!<br>"; } } |
然后在相同目录下再定义另一个类ClassB,文件名为ClassB.class.php,ClassB继承ClassA
<?php
代码如下 | 复制代码 |
class ClassB extends ClassA { public function funb(){
echo "classB also loaded successfully!<br>"; } } |
最后在与上面两个文件相同目录下再定义一个autoload.php(这个文件名随便取)
代码如下 | 复制代码 |
<?php function __autoload($class_name) { require_once ("./".ucfirst($class_name).'.class.php');//ucfirst使类名首字母转换为大写 } $obj = new ClassB(); $obj->funa(); $obj->funb(); ?> |
运行autoload.php的程序,看结果:
classA loaded successfully!
classB also loaded successfully!
例子: 文件夹下有这样几个文件:
User.class.php;
Person.class.php;
Message.class.php;
index.php;
我们就可以这样操作
代码如下 | 复制代码 |
index.php function __autoload($className){ //ucfirst() 将字符串首字母变为大写 include("ucfirst($className)".class.".php"); } /************** |
*例如:在index.php文件中实例化User.class.php中的User类,如果不存在则自动调用__autoload()函数
代码如下 | 复制代码 |
*,将类名User作为参数传入 *************/ $user=new User(); //通过自动加载类调用User.class.php文件 $person=new Person(); //通过自动加载类调用Person.class.php文件 $Message=new Message(); //通过自动加载类调用Message.classphp文件 function="" message="new" person="new" user="new"> |
因为在PHP中不能存在同名的函数,所以在同一个类中也就不能定义重名的方法。这里所说的重载是指在 子类中可以定义和父类同名的方法从而覆盖从父类中继承过来的方法。
子类中重载父类的方法
代码如下 | 复制代码 |
|
重写方法与访问权限
子类中的覆盖方法不能使用比父类中被覆盖方法更严格的访问权限。
如果父类中的方法的访问权限是protected,那么子类中重写的方法的权限就要是protected或者public; 如果父类中的方法是public,那么子类要重写的方法的权限就只能是public。也许这也就是为什么子类可 以继承父类的私有成员,但却不能使用的原因吧。
重写时的参数数量
子类可以拥有与父类不同的参数数量,如下面的构造方法中,多添加了一个参数$age。
代码如下 | 复制代码 |
<?php class Student extends Person{
public $name; public function __construct($name="",$age=25){ $this->age =$age; } public function say(){ echo "我叫".$this->name .",今年".$this->age."岁了" ; } } |
构造函数重写
上面提到的“重写时的参数数量”就已经实现了子类对父类的构造函数进行了重写,但这不是一种好的写 法,如果仔细观察,你会发现,上面子类Student对父类Person构造函数的重写,其实就是在父类的构造 函数的基础上多添加了一个参数,但是又把父类原有的参数照写一遍,因为父类Person的构造函数只有一 个参数,所以我们照写一遍不觉得有什么麻烦,但是如果参数不止一个,而是几个或者更多,那么你就会 发现它的繁琐之处,那么有没有办法可以简化这个问题呢?答案是肯定的,可通过使用"parent::方法名" 在子类的重载方法中调用父类中被它覆盖的方法。如使用"parent::__construct()"调用父类中被覆盖的 构造方法,其它方法的类似,于是上面的代码可以简化为:
代码如下 | 复制代码 |
<?php class Student extends Person{
public $name; public $age; parent::__construct($name,$age); $this->age =$age; public function say(){ echo ",今年".$this->age."岁了" ; } |
下再看一个实例
PHP5重写方法
先设置一个父类,这个父类是 “Dog”类,这个类描述了dog的特性。
Dog有2个眼睛,会跑,会叫。就这样描述先。
我养了一直狗,是只小狗,符合Dog类的特性,但有所不同。
我的小狗有名字,我的小狗太小了,不会大声的叫,只会哼哼。
我们用继承的概念去实现这个设计。
代码如下 | 复制代码 |
<? 程序运行结果: dog have 2 eyes. 狗狗 have 2 eyes. |
重写方法与访问权限
子类中的覆盖方法不能使用比父类中被覆盖方法更严格的访问权限。
父类为public 子类为 private时。
代码如下 | 复制代码 |
<? class MyDog extends Dog { ?> 程序运行结果: |
父类为public 子类为 protected时。
代码如下 | 复制代码 |
<? class MyDog extends Dog { ?> 程序运行结果: Fatal error: Access level to MyDog::getEyeNumber() must be public (as in class Dog) in E:PHPProjectstest.php on line 15 |
重写时的参数数量
子类可以拥有与父类不同的参数数量。(这点与java不同,PHP是弱类型语言。)
代码如下 | 复制代码 |
$myDog = new MyDog(); 程序运行结果: my dog hava 3 eyes. |
构造函数重写
下面这个例子中,父类和子类都有自己的构造函数,当子类被实例化时,子类的构造函数被调用,而父类的构造函数没有被调用,请对比第一节的构造函数继承。
代码如下 | 复制代码 |
<? 程序运行结果: I am a Dog . |
注:这点和Java不同,在java中构造函数是不能被继承的,而且子类实例化时,子类的构造函数被调用,父类的构造函数也会调用。
在php中我们函数传值就比较简单了,但可能有些朋友地天真无邪函数传址或引用搞不明白,下面小编来给各位介绍在php中函数传值与传址(引用)介绍,希望对各位有所帮助。php中引用的用法:
1. 变量的引用赋值: $a = &$b
2. 函数调用时的引用参数传递
1) 早期php是在调用时通过&符号传递引用类型的变量, 比如: func(&$arg);
2) 后来, 函数的引用类型参数被规定为需要在函数声明时定义, 不如: function func(&$arg);
注: 引用声明时定义引用类型参数后, 运行时引用参数传递被废弃, 需要在php.ini中增加allow_call_time_pass_reference来开启.
3. 函数返回引用类型, 这种应用方式需要声明函数时, 在函数名前增加&符号, 并且, 在调用时, 要用引用赋值方式, 比如:
代码如下 | 复制代码 |
|
简而言之, 就是让函数的返回一个引用类型的值, 一个更实际的例子:
代码如下 | 复制代码 |
$a = 1; |
php函数传值的几个细节
代码如下 | 复制代码 |
//一、基本数据类型的传值 /* **************************************************** */ function testvar($k){ $k = 40; } $c = 30; //给一个函数参数传一个基本数据类型(整型,布尔,字符 ...), 实际上传的就是值 ; testvar($c); echo $c;//结果是:30 function testvar2(&$k){ $k = 40; } $e = 30; //给一个函数参数传一个基本数据类型(整型,布尔,字符 ...), 实际上传的y就是地址 ; testvar2($e); echo $e;//结果是:40 /* **************************************************** */ //二、数组(默认情况下是复制一份数据),如要传地址则&$arr. $arr1 = array(-1,5,0); function testArr($arr){ for($i=0;$i<count ($arr);$i++){ for($j=$i+1;$j<count($arr);$j++){ if($arr[$i]>$arr[$j]){ $temp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $temp; } } } print_r($arr); //结果:Array ( [0] => -1 [1] => 0 [2] => 5 ) } testArr($arr1); print_r($arr1); //结果:Array ( [0] => -1 [1] => 5 [2] => 0 ) function testArr2(&$arr){ for($i=0;$i</count><count ($arr);$i++){ for($j=$i+1;$j<count($arr);$j++){ if($arr[$i]>$arr[$j]){ $temp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $temp; } } } } testArr($arr1); print_r($arr1); //结果:Array ( [0] => -1 [1] => 0 [2] => 5 ) /* **************************************************** */ //三、对象数据类型传值 class person{ public $name; public $age; } $a = new person(); $a->name = '小明'; $a->age = '20'; //变量a在存的是对象的地址,把a赋给b这个变量,实际上就是赋了一个地址。 $b = $a; $b->age = 30; //echo $a->age.$b->age;//结果是:30 30 //给一个函数参数传一个对象, 实际上传的是这个对象的地址; function test($k){ $k->age =40; } //调用 test($b); //echo $a->age.$b->age;//结果是:40 40 </count> |
php函数的传值与传址有什么区别呢?对于这样的问题我喜欢通过程序演示来获取答案。让我们来看一个演示记录吧!
代码如下 | 复制代码 |
<?php $i=100; function func($n){ $n=$n+100; return $n; } echo '1)函数传值前变量$i的值:'.$i.'<br>'; echo '2)传值后函数的返回值:'.func($i).'<br>'; echo '3)函数传值后变量$i的值:'.$i.'<br>'; echo '4)函数传址前变量$i的值:'.$i.'<br>'; echo '5)传址后函数的返回值:'.func(&$i).'<br>'; echo '6)函数传址后变量$i的值:'.$i.'<br>'; //echo func($i).'<br>'; ?> |
程序输出:
1)函数传值前变量$i的值:100
2)传值后函数的返回值:200
3)函数传值后变量$i的值:100
4)函数传址前变量$i的值:100
5)传址后函数的返回值:200
6)函数传址后变量$i的值:200
解释:
1)直接输出一个赋值$i=100的变量
2)func函数传值返回一个算术相加的结果$=100+100
3)func函数传值,其作用区域只限于函数内部,对外面的变量$i不会有影响
4)同上
5)func函数参数前多了个"&"符,表示传址,和2)一样,返回值是函数的算术运行结果。
6)func(&$i),变量指针指向$i的位置,可以理解为是对变量$i的操作,$i=$i+100;这时变量$i已经相当于重新被赋值
如果去掉上面代码的第18行注释,会得什么结果呢?
小结:传址为了在执行函数的同时,同时改变函数参数值,而传值不期望改变。
相关文章
Ecshop提示Only variables should be passed by reference in错误
在安装好ecshop软件之后我们打开首页时提示Only variables should be passed by reference in错误了,碰到这个问题是什么原因呢?下面我们就一起来看看解决办法吧。...2016-11-25- 409错误是什么?http 409错误怎么解决呢?不少站长在遇到这个错误代码之后都一筹莫展,本次一聚教程网为大家带来了详细的说明,快来看看吧。 409错误是什么: HTTP 40...2017-01-22
- 414错误是HTTP协议状态码中的一种,很多都还不知道414错误是什么,以及不知道怎么解决414错误,那么就来看看小编带来的介绍吧。 414错误是什么: HTTP 414错误,(Requ...2017-01-22
- http 405错误是什么?http 405错误怎么解决?相信很多站长都在找这两个问题的答案,本次小编为大家带来了详细的教程,快来看看吧。 405错误是什么: HTTP 405错误是H...2017-01-22
- 401是HTTP状态码的一种,属于“请示错误”,表示请求可能出错,已妨碍了服务器对请求的处理。具体的401错误是指:未授权,请求要求进行身份验证。登录后,服务器可能会返回对页面...2017-01-22
- http 402错误是什么?402错误较为少见,一般不轻易出现,下面小编就来告诉大家402错误是什么吧。 HTTP 402错误是HTTP状态码的一种,表示“要求付费”; 所求的...2017-01-22
- 411错误是HTTP协议状态码的一种,很多人都还不知道411错误是什么,本次一聚教程网将为大家进行解答,并且告诉大家411错误怎么解决。 411错误是什么: HTTP 411错误,(Lengt...2017-01-22
- Apache status 503 的原因大致有如下几种情况 : 1、 CPU 负载过高,服务器响应不过来,返回503 2、 系统连接数超限,超过MaxVhostClients的上限,返回503 3、 单IP连接数超限,超过M...2016-01-28
- 403错误是网站访问过程中,常见的错误提示。资源不可用,服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致,比如IIS或者apache设置了访问权限...2017-01-22
- 403错误是HTTP状态码的一种,属于“请示错误”,表示服务器拒绝请求。如果在搜索引擎尝试抓取您网站上的有效网页时显示此状态代码,那么,这可能是您的服务器或主机拒绝搜索...2017-01-22
- 412错误是什么?412错误怎么解决?本次一聚教程网将为大家带来详细的介绍,帮助大家全面了解412错误的意思以及解决412错误的方法。 412错误是什么: HTTP 412错误,(Precond...2017-01-22
- 相信很多站长都遇到过这样一个问题,访问页面时出现408错误,下面一聚教程网将为大家介绍408错误出现的原因以及408错误的解决办法。 HTTP 408错误出现原因: HTT...2017-01-22
- HTTP 406错误是HTTP协议状态码的一种,表示无法使用请求的内容特性来响应请求的网页。一般是指客户端浏览器不接受所请求页面的 MIME 类型。 而MIME类型是在把输出...2017-01-22
- 407错误是什么?407错误怎么解决?不少站长都遇到过407错误,下面小编将告诉大家如何处理407错误。 407错误是什么: HTTP 407错误是HTTP协议状态码的一种,表示需要代...2017-01-22
- 410错误是HTTP协议状态码的一种,本次一聚教程网将为大家详细介绍HTTP 410错误是什么,以及410错误的解决办法。 410错误是什么: HTTP 410错误是HTTP协议状态码的...2017-01-22
- photoshop软件是一款专业的图像设计软件了,但对电脑的要求也是越高越好的,如果配置一般打开ps会比较慢了,那么photoshop打开很慢怎么办呢,下面来看问题解决办法。 1、...2016-09-14
- 每当遇到http错误代码为400,代表客户端发起的请求不符合服务器对请求的某些限制,或者请求本身存在一定的错误,那么HTTP 400错误怎么解决呢?请看下文介绍。 目前400错...2017-01-22
PHP Fatal error: Cannot use object of type stdClass as array in错误
下面一起来看看在php开发中碰到PHP Fatal error: Cannot use object of type stdClass as array in错误问题的解决办法吧。 普通的数组出现如下错误 代码...2016-11-25- JQuery使我们在开发Ajax应用程序的时候提高了效率,减少了许多兼容性问题,我们在Ajax项目中,遇到ajax异步获取数据出错怎么办,我们可以通过捕捉error事件来获取出错的信息。在没给大家介绍正文之前先给分享Jquery中AJAX参...2015-11-24
- 自己用的小PHP应用,使用curl抓网页下来处理,为了穿墙方便,使用Privoxy作为代理,便于选择哪些网站使用proxy、哪些不用。但今天却遇到了奇怪的问题,访问google baidu这些网站居然都返回403错误,而访问其他的一些网站没事,如果...2014-05-31