偷得浮生半日闲--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种:
- 布尔(boolean),与js不同的是,转化为string类型时,false转化为'',而true转化为'1'。
- 整型(integer)
- 浮点(float or double),同其它语言一样,(0.1 + 0.7) * 10是不等于8的。
-
字符串(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种:
-
数组(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数组,不覆盖相同键。
值得注意的是,作为一个复杂对象,数组的赋值并不是通常理解的地址或引用拷贝,而是值拷贝。 - 对象(object)
-
数组(array),其实更接近于通常意义上的Map,并且是有序的。
-
特殊类型2种:
-
资源(resource),顾名思义,就是各种外部资源,如数据库连接,文件等等,可用
get_resource_type
函数获得具体信息。 - 空类型(NULL)
-
资源(resource),顾名思义,就是各种外部资源,如数据库连接,文件等等,可用
-
伪类型若干,并不存在这样的类型,只是在文档中便于理解而用的:
- 混合型(mixed)
- 数值(number)
- 回调(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。