虚度人生

导航

偷得浮生半日闲--php入门

先解释一下题目,最近这段时间突然就忙了起来,预计之后还要更忙,一天忙来忙去却不知道在忙些啥,人也一直很累,生理心理都是。
Android这块公司已经安排了人手,暂时就先放一放,等js真正熟悉了,再回头来学,先入门一下php,作为调剂,提高点情绪。

PHP,大名是HyperText Preprocessor,与ASP,JSP并称为web 3p。但从本身名字来看,ASP和JSP的P,指的都是Pages,因此还是有些特殊的。

先来看一下语法:

<?php echo 'This is from php' ?>
<p>This is html</p>
<?php echo 'We omitted the last closing tag';

如果php代码是在一个文件最后部分,那它的闭合标签是可以省略的,并且官方提倡在某些情况(some cases)下省略,但这某些情况具体指哪些,不省略又会带来什么问题,就只能等待日后自己发掘了。 官方永远是官方,无论是在政治圈还是IT圈,说的话都是永远一半,不可不信,不可全信。

如同asp,js一样,php也是一种弱类型语言,即是指一个变量可以赋值为字符串,也可以赋值为布尔值。

按变量类型来分:

  • 标量类型(scalar types),或者说原生类型(primitive types)4种:
    1. 布尔(boolean),与js不同的是,转化为string类型时,false转化为'',而true转化为'1'。
    2. 整型(integer)
    3. 浮点(float or double),同其它语言一样,(0.1 + 0.7) * 10是不等于8的。
    4. 字符串(string),比较复杂,有4种表示方式,单引号,双引号,定界符(heredoc),定界符(nowdoc)。
      单引号,不会解析字符串中的变量以及转义符,正常输出。
      双引号,会解析字符串中的变量以及转义符,如$pen = 'ball'; echo "this is $pen"会输出this is ball,复杂的情况,比如多层嵌套,可求助于'{}'。
      heredoc,如<<<XXX...XXX;的格式,作用与双引号大致相同,要注意的是结尾的定界符必须是另起一行,并且不能包含除';’外的多余字符,并且heredoc不能用来初始化类的属性。
      nowdoc,如<<<'XXX'...XXX;的格式,作用与单引号大致相同,并且能用来初始化任何静态数据(static data context),具体static data context指的是什么,现在也不是很明确。
      一般来说,很少会用到定界符的表示方法,只要作个了解即可。
      关于字符串的下标索引,与js也稍有不同,非数值的下标,都会当成0处理,而数值下标越界的话,则会抛出一个E_NOTICE错误。
  • 复合类型(compound types)2种:
    1. 数组(array),其实更接近于通常意义上的Map,并且是有序的。
      支持下标为空的赋值,表示以最大的整数key+1为key赋值。
      删除使用unset函数,注意,unset并不会重置最大下标,如 $arr = array(1); unset($arr[0]); $arr[] = 2; print_r($arr);,输出的是’Array ( [1] => 2 )',其余的常用函数可见这里。
      php的数组有一个很棒的特性,如同c++支持操作符重载一样,数组本身支持操作符,如$a + $b表示将$b数组合并到$a数组,不覆盖相同键。
      值得注意的是,作为一个复杂对象,数组的赋值并不是通常理解的地址或引用拷贝,而是值拷贝
    2. 对象(object)
  • 特殊类型2种:
    1. 资源(resource),顾名思义,就是各种外部资源,如数据库连接,文件等等,可用get_resource_type函数获得具体信息。
    2. 空类型(NULL)
  • 伪类型若干,并不存在这样的类型,只是在文档中便于理解而用的:
    1. 混合型(mixed)
    2. 数值(number)
    3. 回调(callback)
可以使用 var_dump 输出包含类型的变量具体信息,或者用 is_xxx 函数来判别是否某类型,不提倡使用 gettype ,返回值可能根据php版本的不同而不同。
在js中,我们经常看到这样的写法, if (x) ... ,在php中,假如$x变量未定义的话会抛出一个E_NOTICE级别的错误,因此一般采用 if (empty($x)) ...if (is_null($x)) ...。 的写法, is_null 只对null或undefined返回true,相反的函数是 isset ,而 empty 对空字符串,空数组,false,0,0.0和'0'都会返回true。
php支持类型的自动隐式转换,当然可以使用type cast或者 settype 函数显式改变类型。

 

按变量作用来分:

  • 普通变量,即$a = 'hi'
  • 可变变量,即$a = 'hi'; $$a = 'world';,同于定义$hi变量,不过这并不是完全动态的,而是在运行到该行代码时,取得$a的值作为该变量的命名。
  • 静态变量,即function t() { static $a = 1; return ++$a; },t函数共享同一个变量$a。
  • 全局变量,即$a = 4; function t() { global $a; $a = 5; },实际上函数内的$a是相当于全局$a的一个引用,等同于$a = 4; function t() { $a = &$GLOBALS['var']; $a = 5; },关于引用的具体解释,看下文。
  • 超全局变量,预定义的全局可见的几个变量,如上文所见的$GLOBALS,详细可见这里。

 

与变量相对,php也拥有常量定义,有两种定义方式:

  • define(CONST, '...'),支持表达式。
  • php5.3之后引入了一种新的方式,const CONST = '...',不支持表达式,并且由于编译时定义的原因,必须写在全局域中。其实除ie外,js也支持这种常量定义的方式。

与别的语言相比,操作符上大同小异,但也有几个比较特殊的,这里介绍一下:

  • @,at符,当在表达式前加入这个符号,表示忽略该表达式的任何错误,常用于打开资源,如$my_file = @file ('non_existent_file') or die ("Failed opening file: error was '$php_errormsg'");
  • `,反引号,表示在shell中执行反引号中的语句并返回其输出,如$output = `ls -la`;

require和include

之所以特别提到这两个东东,是因为这两个看似函数的东东并不是函数,而归为控制结构,并且使用频率很高。
千万注意,require,并不像网上到处说的是在编译时处理的,无论判断条件真假,都会载入,经测试,require和include一样,也支持判断结构,也支持变量为参数,也可以出现在函数内部,区别就是载入出错的时候require会抛出错误,停止执行,而include只是给个警告。猜测可能是新版的php作了更改,具体哪个版本开始支持的就不得而知了。
尽信书不如无书,经验教训啊,同样的情况在学习js的时候也遇到几次,希望大家能尽量的自己测试一下,不要哪里抄一段就完事。
allow_url_fopen配置为真的时候,require和include甚至支持从远程载入文件

php的函数

  • 支持参数引用,如function test(&$arg) {}
  • 支持c++格式的参数默认值,如function test($arg = 'hi') {}
  • php5.1以后支持参数类型,但与js不同的是,调用函数时参数类型不会隐式自动转换,null也不行,如function test(string $s) {}; test(3);会报一个Fatal Error。
  • 在php中,很多情况下,都可以用函数名称的字符串来代表函数本身,如function test() {};$str = 'test';$str();

php的类和接口,继承自c++的风格,基本上该有的都有了,可见性(visibility),构造析构函数,静态(static),抽象(abstract),接口(interface),final。

  • 有public, protected, private这3个修饰符,同时,为了兼容php4,还支持var $var1 = 1;写法,等同于pulbic。
  • php5还引入了一个比较有意思的函数,__autoload,不需要require 'A.php'加载类文件,在new一个对象的时候,会自动检测是否有该类定义,如没有则自动调用__autoload函数,当然,由于需要IO操作,有时会造成性能开销较大。
  • 支持静态属性和方法,加上static关键字,存取的时候采用域操作符'::'。
  • php里也有overloading的概念,但这个和其它oo语言的重载概念完全不一样。一般意义上的重载是指我们可以在一个类里定义多个重名的方法,然后根据调用时传入的参数不同自动选择相应方法。而php则是提供了几个方法,当存取不可见属性或者调用某些特定方法的时候,会自动调用相应方法,如同js中的getter,更类似于回调函数,参看魔术方法。
  • php有一些被称为魔术方法的函数,以双下划线开头'__',会在某些情况下被自动调用。
    • public function __construct(),构造函数,假如没有找到构造函数的话,php会寻找类的同名函数作为构造函数,函数不会自动调用父类的相应方法,需要用parent::__construct();显式调用。
    • public function __destruct(),构造函数,同样需要parent::__destruct();显式调用父类方法,禁止抛出异常,否则会导致一个严重错误。
    • public function __set($name, $value),在设置未定义属性时自动调用。
    • public function __get($name),在读取未定义属性时自动调用。
    • public function __call($name, $arguments),调用未定义非静态方法时自动调用。
    • public static function __callStatic($name, $arguments),调用未定义静态方法时自动调用。
    • public function __isset($name),调用isset时自动调用。
    • public function __unset($name),调用unset时自动调用。
    • public function __sleep(),调用serialize时自动调用。
    • public function __wakeup(),调用unserialize时自动调用。
    • public function __toString(),和js中的toString一样,在需要隐式转换为String时自动调用。
    • public static function __set_state($arr),在调用var_export时自动调用,恕我愚钝,实在想不出这个函数的作用。
    • public function __clone(),在调用clone时自动调用。默认情况下,php都是采用浅拷贝,在需要深拷贝的时候则需要我们自己覆写__clone的方法。
  • php的对象同数组一样,同样可以用==,!=,===,!==这几个比较操作符来直接比较,使用比较方便。
  • 静态方法的延迟绑定,是php5.3之后提供的一个特性。在普通方法中,$this会根据具体运行的情况而指向不同的当前对象,但是假如父类和子类都有一个相同的静态方法,如何根据实际情况选择调用,看代码。
    class A {
    public static function who() {
    echo __CLASS__;
    }
    public static function test() {
    self::who();
    }
    public static function test2() {
    static::who();
    }
    }
    class B extends A {
    public static function who() {
    echo __CLASS__;
    }
    }
    A::test();// A
    B::test();// A
    A::test2(); // A
    B::test2(); // B
    
    ,test方法输出始终是'A',而test2输出则是根据调用的类来判定。

php的引用(reference)是一个非常重要的概念,需要清楚掌握,才能较深刻理解一些代码的含义。

首先,要区别引用与指针(pointer)的概念。指针是指向内存地址的变量,如

int *a, *b;
int c = 1, d = 2;
a = &c;
b = &c;
*a = 3; // *b = 3, c = 3
a = &d;
*a = 4; // *b = 3, c = 3
, 这里声明了两个指针a和b,它们都指向c变量所在的内存区域,当给*a赋值时,即改变a所指向内存区域的值,*b和c都变成了3,之后将d变量所在内存区域的地址赋给a,即a指向了d所在内存,这时给*a赋值并不会影响*b和c。

 

引用是指对同一变量内容取的不同名称,如

$a = 1;
$b = &$a;
$b = 2; // $a = 2
unset($b);// $a = 2
, 这里$b是$a的引用,表示$a和$b只是值为1的变量的不同名称,就像文件的快捷方式,所以改变$b的值,$a的值也随之改变。而unset($b)表示取消了$b这个名称,$a仍是2。

 

在php中,除了变量引用外还有参数引用和返回值引用,先看代码。

function &t() {
      return 5;
}
function t2(&$a) {
}
function t3() {
      return 5;
}
t();
t2();
t2($a = 5);
t2(t3());
,调用t会报一个notice错误,第一次调用t2时会报Fatal Error,为什么?仔细理解引用的定义,是指向某个变量内容的不同名称,当文件都没有的时候,哪来的快捷方式。 但是,t2的第二次和第三次调用并不像官网说的那样会报错,而是正常执行,不理解为什么会产生这样的情况,我的测试环境是php 5.2.12,哪位同学指点一下。

 

有一种情况有些特殊,如$b = &$a; $b = 3; var_dump($a);,$a的值为3,当赋值,参数传递或返回一个未定义变量的引用时,系统会自动创建该变量。

好,最后再来看一段代码,作个总结,

class A {
     var $x = 1;
}
$a = new A;
$b = &$a;
$c = $a;
$a->x = 10;
var_dump($b); // object(A)#1 (1) { ["x"]=>int(10) }
var_dump($c); // object(A)#1 (1) { ["x"]=>int(10) }
$a = array(1);
var_dump($b); // array(1) { [0]=>int(1) }
var_dump($c); // object(A)#1 (1) { ["x"]=>int(10) }
$c = $a;
$a[0] = 10;
var_dump($b); // array(1) { [0]=>int(10) }
var_dump($c); // array(1) { [0]=>int(1) }
,$b是$a的引用,而$c是$a直接赋值,一开始$a指向A的一个对象,因此赋值给$c的是对象地址,所以第一次改动,$b和$c都会相应变化。 接下来,$a是一个数组,赋值给$c的时候采用了值传递,因此$a的改动不会影响到$c。

 

posted on 2010-04-30 16:32  nozer0  阅读(681)  评论(0编辑  收藏  举报