面向对象程序设计是一种程序设计范型,同时也是一种程序开发方法。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性,灵活性和可扩展性。
{函数式编程 如LISP ,
命令式编程 { 面向过程, 面向对象}
}
类是对象的抽象组织,对象是类的具体存在。
class person{
public $name;
public $gender;
public function say(){
echo $this->name," is ",$this->gender;
}
}
$student=new person();
$student->name="Tom";
$student->gender='male';
$student->say();
$teacher=new person();
$teacher->name='Kate';
$teacher->gender='female';
$teacher->say();
print_r((array)$student);
var_dump($student);
$str=serialize($student);
echo $str;
file_put_contents('store.txt',$str);
$str=file_get_contents('store.txt');
$student=unserialize($str);
$student->say();
所谓序列化,就是把保存在内存中的各种对象状态(属性)保存起来,并且在需要时可以还原出来。
对象序列化后,存储的只是只是对象的属性。 对象就是数据,对象本身不包含方法。但是对象有一个“指针”指向一个类,这个类里可以有方法。
序列化和反序列化时都需要包含类的对象的定义,否则可能返回不正确的结果。
#zend/zend.h
typedef union_zvalue_value{
long lval;
double dval;
struct{
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} zvalue_value;
#zend/zend.h
typedef struct_zend_object{
zend_class_entry *ce;
HashTable *properties;
HashTable *guards;
} zend_object;
对象是一种很普通的变量,不同的是其携带了对象的属性和类的入口。
$student_arr=array('name'=>'Tom','gender'=>'male');
echo "\n";
echo serialize($student_arr);
对象和数组的区别在于:对象还有个指针,指向了它所属的类。
class person{
public $name;
public $gender;
public function say(){
echo $this->name,"\tis ",$this->gender,"\r\n";
}
}
class family{
public $people;
public $location;
public function __construct($p,$loc){
$this->people=$p;
$this->location=$loc;
}
}
$student=new person();
$student->name='Tom';
$student->gender='male';
$student->say();
$tom=new family($student,'peking');
echo serialize($student);
$student_arr=array('name'=>'Tom','gender'=>'male');
echo "\n";
echo serialize($student_arr);
print_r($tom);
echo "\n";
echo serialize($tom);
当一个对象的实例变量应用其他对象时,序列化该对象时也会对引用对象进行序列化。
语法糖:指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。 如魔术方法的写法。
class Account{
private $user=1;
private $pwd=2;
public function __set($name,$value){
echo "Setting $name to $value \r\n";
$this->$name=$value;
}
public function __get($name){
if(!isset($this->$name)){
echo '未设置';
$this->$name='正在为你设置默认值';
}
return $this->$name;
}
}
$a=new Account();
echo $a->user;
$a->name=5;
echo $a->name;
echo $a->big;
PHP一个类中只允许有一个构造函数。
PHP中的“重载”是指动态地“创建”类属性和方法。__set和__get方法被归到重载里。
定义了__get和__set方法,直接调用私有属性 和在对外的public方法中操作private属性 的原理一样,只不过操作起来更简单。
class Account{
public function __call($name,$arguments){
switch(count($arguments)){
case 2:
echo $arguments[0]*$arguments[1],PHP_EOL;
break;
case 3:
echo array_sum($arguments),PHP_EOL;
break;
default:
echo '参数不对',PHP_EOL;
break;
}
}
}
$a=new Account();
echo $a->make(5);
echo $a->make(5,6);
echo $a->make(5,6,7);
abstract class ActiveRecord{
protected static $table;
protected $fieldvalues;
public $select;
static function findById($id){
$query="select * from "
.static::$table
." where id=$id";
return self::createDomain($query);
}
function __get($fieldname){
return $this->fieldvalues[$fieldname];
}
static function __callStatic($method,$args){
$field=preg_replace('/^findBy(\w*)$/','${1}',$method);
$query="select * from "
.static::$table
." where $field='$args[0]'";;
return self::createDomain($query);
}
private static function createDomain($query){
$klass=get_called_class();
$domain=new $klass();
$domain->fieldvalues=array();
$domain->select=$query;
foreach($klass::$fields as $field=>$type){
$domain->fieldvalues[$field]='ATODO: set from sql result';
}
return $domain;
}
}
class Customer extends ActiveRecord{
protected static $table='custdb';
protected static $fields=array(
'id'=>'int',
'email'=>'varchar',
'lastname'=>'varchar',
);
}
class Sale extends ActiveRecord{
protected static $table='salesdb';
protected static $fields=array(
'id'=>'int',
'item'=>'varchar',
'qty'=>'int'
);
}
echo Customer::findById(111)->select;
echo Customer::findById(111)->email;
echo Customer::findByLastname('liu')->select;
只有实现了__toString方法才能直接echo 对象。
class Account{
public $user=1;
public $pwd=2;
public function __toString(){
return "当前对象的用户是{$this->user},密码是{$this->pwd}";
}
}
$a=new Account();
echo $a;
echo PHP_EOL;
print_r($a);
继承是类级别的复用,多态是方法级别的复用。
class person{
public $name='Tom';
public $gender;
static $money=10000;
public function __construct(){
echo '这里是父类',PHP_EOL;
}
public function say(){
echo $this->name,"\t is ",$this->gender,"\r\n";
}
}
class family extends person{
public $name;
public $gender;
public $age;
static $money=100000;
public function __construct(){
parent::__construct();
echo '这里是子类',PHP_EOL;
}
public function say(){
parent::say();
echo $this->name,"\t is \t",$this->gender,", and is \t",$this->age,PHP_EOL;
}
public function cry(){
echo parent::$money,PHP_EOL;
echo '%>_<%',PHP_EOL;
echo self::$money,PHP_EOL;
echo '(*^-^*)';
}
}
$poor=new family();
$poor->name='Lee';
$poor->gender='female';
$poor->age=25;
$poor->say();
$poor->cry();
从这个例子可以看出 父类直接共享了子类的成员值。 在开发时,设置最严格的报错等级,在部署时可适当降低。
低耦合指模块与模块之间,尽可能地使模块间独立存在;模块与模块之间的接口尽量少而简单。
解耦是要解除模块与模块之间的依赖。
继承和组合均可的情况下,倾向用组合。
继承破坏封装性。继承是紧耦合的。继承扩展复杂。不恰当地使用继承可能违反现实世界中的逻辑。
底层代码多用组合,顶层、业务层代码多用继承。底层用组合可以提高效率,避免对象臃肿。顶层代码用继承可以提高灵活性,让业务更方便。
多重继承:一个类可以同时继承多个父类,组合两个父类的功能。 PHP中的Traits既可以使单继承模式的语言获得剁成继承的灵活,有可以避免多重继承带来的种种问题。
class car{
public function addoil(){
echo "Sdd oil \r\n" ;
}
}
class bmw extends car{
}
class benz{
public $car;
public function __construct(){
$this->car=new car();
}
public function addoil(){
$this->car->addoil();
}
}
$bmw=new bmw();
$bmw->addoil();
$benz=new benz();
$benz->addoil();
重载不是面向对象里的东西,它数据域多态的一种表现形式。
多态性是一种通过多种状态或者阶段描述相同对象的编程方式。实际开发中,只要关心一个接口或者基类的编程,而不必关心一个对象所属于的具体类。
class employee{
protected function working(){
echo '本方法需要重载才能运行';
}
}
class teacher extends employee{
public function working(){
echo '教书';
}
}
class coder extends employee{
public function working(){
echo '敲代码';
}
}
function doprint($obj){
if(get_class($obj)=='employee'){
echo 'Error';
}else{
$obj->working();
}
}
doprint(new teacher());
doprint(new coder());
doprint(new employee());
interface employee{
public function working();
}
class teacher implements employee{
public function working(){
echo '教书';
}
}
class coder implements employee{
public function working(){
echo '敲代码';
}
}
function doprint(employee $i){
$i->working();
}
$a=new teacher();
$b=new coder();
doprint($a);
doprint($b);
接口中所有的方法都是抽象的,没有程序体。
接口的方法必须被全部实现,否则将报错。
interface mobile{
public function run();
}
class plain implements mobile {
public function run(){
echo "我是飞机";
}
public function fly(){
echo "飞行";
}
}
class car implements mobile{
public function run(){
echo "我是汽车";
}
}
class machine{
function demo(mobile $a){
$a->fly();
}
}
$obj=new machine();
$obj->demo(new plain());
$obj->demo(new car());
trait Hello{
public function sayHello(){
echo 'Hello ';
}
}
trait World{
public function sayWorld(){
echo 'World';
}
}
class MyHelloWorld{
use Hello,World;
public function sayExcalamationMark(){
echo '!';
}
}
$o=new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExcalamationMark();
PHP中的接口没有契约限制,缺少足够多的内部接口。
反射是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类,方法,属性等详细信息,包括注释。
这种动态获取信息以及动态调用对象方法的功能成为反射API。
class person{
public $name;
public $gender;
public function say(){
echo $this->name,"t is ",$this>gender,"\r\n";
}
public function __set($name,$value){
echo "Setting $name to $value \r\n";
$this->$name=$value;
}
public function __get($name){
if(!isset($this->$name)){
echo '未设置';
$this->$name="正在为你设置默认值";
}
return $this->$name;
}
}
$student=new person();
$student->name='Tom';
$student->gender='male';
$student->age=24;
$reflect=new ReflectionObject($student);
$props=$reflect->getProperties();
foreach($props as $prop){
print $prop->getName()."\n";
}
$m=$reflect->getMethods();
foreach($m as $prop){
print $prop->getName()."\n";
}
使用class函数,返回对象属性的关联数组以及更多的信息
//返回对象属性的关联数组
var_dump(get_object_vars($student));
//类属性
var_dump(get_class_vars(get_class($student)));
//返回由类的方法名组成的数组
var_dump(get_class_methods(get_class($student)));
//获取对象属性列表所属的类
echo get_class($student);
//反射获取类的原型
$obj=new ReflectionClass('person');
$className=$obj->getName();
$Methods=$Properties=array();
foreach($obj->getProperties() as $v){
$Properties[$v->getName()]=$v;
}
foreach($obj->getMethods() as $v){
$Methods[$v->getName()]=$v;
}
echo "class {$className} \n {\n";
is_array($Properties)&&ksort($Properties);
foreach($Properties as $k=>$v){
echo "\t";
echo $v->isPublic()?'Public':'',
$v->isPrivate()?'private':'',
$v->isProtected()?'protected':'',
$v->isStatic()?' static':'';
echo "\t{$k}\n";
}
echo "\n";
if(is_array($Methods)) ksort($Methods);
foreach($Methods as $k=>$v){
echo "\tfunction{$k}(){}\n";
}
echo "}\n";
反射可以用于文档生成,做hook实现插件功能,做动态代理。
class mysql{
function connect($db){
echo "连接到数据库${db[0]} \r\r";
}
}
class sqlproxy{
private $target;
function __construct($tar){
$this->target[]=new $tar();
}
function __call($name,$args){
foreach($this->target as $obj){
$r=new ReflectionClass($obj);
if($method=$r->getMethod($name)){
if($method->isPublic()&& !$method->isAbstract()){
echo "方法前拦截记录Log\r\n";
$method->invoke($obj,$args);
echo "方法后拦截\r\n";
}
}
}
}
}
$obj=new sqlproxy('mysql');
$obj->connect('member');
PHP把许多异常看作错误。
在PHP里,遇到任何自身错误都会触发一个错误,而不是抛出异常(对于一些情况,会同时抛出异常和错误)。
$a=null;
try{
$a=5/0;
echo $a,PHP_EOL;
}catch(exception $e){
$e->getMessage();
$a=-1;
}
echo $a;
PHP通常是无法自动捕获有意义的异常,它把所有不正确的情况都视作错误,你要想捕获这个异常,就得使用if else 结构,保证代码是正常的,然后判断如果xxx,则手工抛出异常,再捕获。
class emailException extends exception{
}
class pwdException extends exception{
function __toString(){
return "Exception{$this->getCode()}:{$this->getMessage()} in File:{$this->getFile()} on line:{$this->getLine()}";
}
}
function reg($reginfo=null){
if(empty($reginfo) || !isset($reginfo)){
throw new Exception("参数非法");
}
if(empty($reginfo['email'])){
throw new emailException("邮件为空");
}
if($reginfo['pwd']!=$reginfo['repwd']){
throw new pwdException("两次密码不一致");
}
echo '注册成功';
}
try{
reg(array('email'=>'bcc@126.com','pwd'=>'abc','repwd'=>'12342'));
}catch(emailException $ee){
echo $ee->getMessage();
}catch(pwdException $ep){
echo $ep;
echo PHP_EOL,'特殊处理';
}catch(Exception $e){
echo $e->getTraceAsString();
echo PHP_EOL,'其他情况,统一处理';
}
注意:exception作为超类应该放在最后捕获,不然捕获这个异常超类后,后面的捕获就终止了。
以下三种情景下会用到异常处理机制。
1 对程序的悲观预测 2 程序的需要和对业务的关注 异常偏重于保护业务数据一致性,并强调对异常业务的处理。
3 语言级别的健壮性要求 可以把异常造成的逻辑中断破坏降低到最小范围内,并经过补救处理后不影响业务逻辑的完整性;乱抛异常和只抛不捕获,或捕获而不补救,会导致数据混乱。
depreccated
notice
warning
fetal error
prase error
$date='2012-12-20';
if(ereg("([0-9]{4}-([0-9]{1,2})-([0-9]{1,2})",$date,$regs)){
echo "$regs[3].$regs[2].$regs[1]";
}else{
echo "Invalid date format:$date";
}
if($i>5){
echo '$i 没有初始化啊',PHP_EOL;
}
$a=array('o'=>2,3,6,8);
echo $a[o];
$result=array_sum($a,3);
echo fun();
echo '致命错误后呢,还会执行吗?'
function customError($errno,$errstr,$errfile,$errline){
echo "<b>错误代码:</b>[${erron}] ${errstr}\r\n";
echo "错误所在的代码行:{$errline} 文件{$errfile}\r\n";
echo " PHP 版本 ",PHP_VERSION,"(",PHP_OS,")\r\n";
}
set_error_handler("customError",E_ALL|E_STRICT);
$a=array('o'=>2,3,45,6);
echo $a[o];
如果使用自定义的set_error_handler接管PHP的错误处理,@将失效,这种错误也会被显示。
可以把“异常”像错误一样使用set_error_handler接管,进而主动抛出异常,来捕获异常和非致命的错误。
function customError($erron,$errstr,$errfile,$errline){
//自定义错误处理时,手动抛出异常
throw new Exception($erron.'|'.$errstr);
}
set_error_handler("customError",E_ALL|E_STRICT);
try{ $a=5/0;}
catch(Exception $e){
echo '错误信息:',$e->getMessage();
}
错误抛出
$divisor=0;
if($divisor==0){
trigger_error("Can not divide by zero",E_USER_ERROR);
}
echo 'break';