回顾PHP的一些基础知识,权当对这么多年学习和使用PHP的提炼和总结。

PHP设计之初作为一门宏定义语言,主要用来处理网页表单,所以被设计为可以内嵌在HTML文件里。为了区分HTML代码,PHP代码块的起始界定标签是<?php,结束标签是?>(PHP5.4版本以后可以使用<?和?>短标签)。对于纯PHP文件,同样需要保留<?php作为起始标识,但建议省略结束的?>标签。

变量/常量

PHP中的变量定义需以美元符号“$”开头,例如: $name = 'tlanyan'。变量名不支持声明变量类型,大小写敏感。合法的变量名以字母、下划线开头,不能以数字、点、加减乘除等特殊符号开头,但是可以以中文汉字开头。

以下是一些变量的声明以及合法性判断:

$name = "tlanyan";   // 合法
$.name = "tlanyan";   // 非法,不能以点开头
$1234 = "tlanyan";   // 非法,不能以数字开头
$myName = "tlanyan"; // 合法
$my-name = "tlanyan"; // 非法,变量名中不能有减号
$名字 = "tlanyan";    // 合法

常量的定义方式有两种:define和const。常量名中的字母必须全部大写,可以用下划线连接。例如:

define('NAME', 'tlanyan');  // 合法
define('NAME_last', 'tlanyan');   // 非法,字母必须全部大写
define('NAME_2', 'tlanyan');     // 合法
define('NAME_中国', 'tlanyan');   // 合法
define('GENDERS', [ 0 => '女', 1 => '男',]);   // define定义常量数组,需要PHP7+

echo NAME;   // 无需加$或引号

const NAME = 'tlanyan';   // 合法,但是如果define已经定义了NAME,运行时会报错
const NAME_ADcdd = 'tlanyan';  // 非法,字母必须全部为大写

const GENDERS = [
    0 => '女',
    1 => '男',
];    // const定义常量数组,需要PHP5.6+

常量定义后,不能更改和取消。defined可以判断一个常量是否定义,constant函数可以取出常量的值(效果同直接取值)。define和const的主要区别是define可以用在判断语句中,但是const不行。如果你需要灵活的方式定义变量,请使用define,否则都建议用const定义常量。

函数

函数的定义以function关键字开头,后接函数名和参数,以大括号包含的函数体结束。代码的定义形式为:

function foo($arg1, $arg2, ...)
{
   // 函数体
}

函数名的要求同变量名,不能以数字开头,不能包含点、加减乘除符号等特殊符号和字符,但是可以用中文命名。不同于变量,函数名不区分大小写

函数参数可以没有,也可以有多个。支持默认参数,但是默认参数后不可出现必须传值的参数。对于基本类型,参数默认按值传递,对象和资源默认引用传参。

函数体可以为空,也可以有多个语句。可以return返回值,也可以不返回。如果函数没有返回值,则默认为null。

下面是一些函数的示例:

function printName($name = 'World')
{
    echo "Hello, $name!";
}

function printName2(array $names) // array参数约束,需要PHP5.1+
{
    print_r($names);
}

function printName3(array $names, callable $filter) // callable参数约束,需要PHP5.4+
{
    print_r(array_map($filter, $names));
}

function sum(...$args)    // 可变参数数组,需要PHP5.6+
{
    return array_sum($args);
}

function printName4(string $name = 'World') // int, float, bool参数类型提示,需要PHP7+
{
    echo "Hello, $name!";
}

function getGreeting(string $name = 'World') : string  // 返回类型声明,需要PHP7+
{
    return "Hello, $name!";
}

function getGreeting2(string $name = null) : ?string // 可空返回类型声明,需要PHP7.1+
{
    return !$name ? null : "Hello, $name!";
}

注意如果限定传参和返回值是bool类型,应该用bool而不能使用boolean,否则运行会报错。

PHP目前为止不支持重载。一种变相的重载实现方式是: 以 …args 定义函数参数,函数体根据参数个数实现不同功能。

操作符

PHP支持常规算术运算符、赋值运算符、位运算操作符等。以下是一些示例:

$a = 2;$b = 3;

echo $a (+|-|*|/|%|**) $b;    // 加减乘除、取模、指数

++$a; --$b;     // 自增和自减

$c = $a + $b;     // 赋值
$d = $a (&,|,^,<<, >>) $b;    // 并、或、异或、左移和右移位运算
$e = ~$a;                   // 取反

echo $a (==|===|!=|<>|<|>|<=|>=|<=>) $b;   // 比较运算符:相等、全等、不等、不等、小于、大于、小于等于、大于等于、太空符(需要PHP7+)

$str1 = 'Hello'; $str2 = 'World';
echo $str1 . ',' . $str2 . '!';   // . 作为字符串连接符
if ($a (|| , && , xor , and, or) !$b) {}   // 逻辑运算符:或、并、异或、并、或、非

$arr1 = [1, 2]; $arr2 = [3, 4];
print_r($arr1 + $arr2);   // 数组合并
var_dump($arr1 !=|!==|<>|==|=== $arr2);   // 数组比较

var_dump($arr1 instanceof int);   // instanceof操作符

@coun($arr1);          //错误控制符@。当@加在函数调用前,函数抛出的错误将被忽略

var_dump(`ls -al`);   // ``, 非单引号, shell执行符号

流程控制

PHP支持常见的分支判断、循环等流程控制。以下是一些示例:

$array = range(0, 10);
$length = count($array);
if ($length > 20) {   // if/elseif/else控制符
    echo '>= 20';
} elseif ($length < 10) {
    echo '< 10';
} else {
    echo '10<= length < =20';
}

$index = 0;
while ($index < $length) { // while循环、continue、break
   if ($index % 2 === 0) {
       continue;
   }
   echo $array[$index], PHP_EOL;
   ++ $index;
   if ($index == 9) {
       break;
    }
}

$index = 0;    // do-while循环
do {
    echo $array[$index], PHP_EOL;
    ++ $index;
} while ($index < $length);

for ($index = 0; $index < $length; ++ $index) {  // for循环
    echo $array[$index], PHP_EOL;
}

foreach ($array as $value) { // foreach循环
    echo $value, PHP_EOL;
}

switch ($length) {  // switch
    case 1:
    case 3:
    case 10:
        echo 'A';
        break;

    case 4:
    case 5:
        echo 'B';
        break;

    default:
        break;
}

类/接口/名字空间/特性

PHP5.3增加了名字空间和静态延迟绑定,PHP5.4增加了特性(trait)功能,标志着PHP对面向对象编程的支持进了一大步。

类在基本类型和数组上进行了拓展,丰富了编程中的类型,同时让开发人员有能力在更高层次上对问题进行建模。

PHP用class关键字对类进行定义,类中可包含属性变量、属性常量、行为方法和特性。以下是定义和使用类的示例:

class People {   // 定义一个类
    const MALE = 1;
    const FEMALE = 0;                // 类常量
    private $age;                     // 私有属性
    public $gender;                  // 共有属性
    public $name;                    // 共有属性

    public __construct($name, $gender = People::MALE)   // 构造函数,类常量作为默认参数,需要PHP5.6+
    {
        $this->name = $name;
        $this->gender = $gender;
    }

    public function getAge()                // 共有方法
    {
        if ($age < 16) { return $this->age + 2;
        }
        if ($age > 35) {
            return $this->age - 3;
        }
        return $this->age;
    }

    private function sleep()                // 私有方法
    {
        ++ $this->age;
    }

    public function __toString()
    {
        return "People [ name: {$this->name}, gender: {$this->gender}]";
    }
}

$me = new People('tlanyan', People::MALE);   // 实例化一个类
echo $me->name;       // 获取类的公有属性
echo $me->age;        // 非法,外部不能获取类的私有属性
echo $me->getAge();   // 调用公有方法
$me->sleep();         // 非法,外部不能调用私有方法

echo $me;             // 调用__toString方法,打印信息

__construct和__toString函数被称为“魔术方法”,常用的魔术方法还包括:__get, __set, __clone, __call等。当对类的对象发送消息时,这些魔术方法赋予拦截的机会,从而做出响应。

接口

编程中很重要的一个原则是面向接口编程,而非面向实现编程。接口规定了实现接口时必须遵守的规则,屏蔽了具体的实现细节。在与其它层交互是,只需要按照既定义的规则对接即可,从而达到解耦的目的。

在PHP中用interface关键字定义接口,接口包含的规则同定义类的成员函数相似,但是接口不能包含成员属性、常量等。以下是一个接口定义和实现:

class Message {
    public $id;
    public $content;
    public $create_at;
}

interface Notify {
    public void notify(Message msg);
}

class SmsNotify implements Notify {
    public void notify(Message msg) {
        // send message using the carrier api
    }
}

接口中只包含规则的签名,具体实现由实现类负责。一个实现类可以同时实现多个接口。

名字空间

为了更好的组织程序代码,也为了避免类名和方法名重复,PHP在5.3版本中增加了对名字空间的支持。名字空间用namespace定义,层次用反斜杠\来分割。名字空间定义必须出现在文件的非注释第一行。以下是名字空间的示例:

// 文件名: Foo.php
namespace tlanyan;    // 定义名字空间,以下定义均位于该名字空间内

const VERSION = '1.0-SNAPSHOT';   // 定义名字空间常量

function greeting() {    // 定义名字空间函数
    echo 'you are calling functions in namespace tlanyan';
}

class Foo {   // 定义名字空间中的类

}

// 在另外一个文件内使用以上定义的内容
require ('Foo.php');

use tlanyan;            // 申明使用tlanyan名字空间中
use tlanyan\Foo;        // 引入Foo类

echo tlanyan\VERSION;   // 调用名字空间中的常量

\tlanyan\greeting();     // 使用完全限定名字空间方式调用函数

$foo = new Foo();     // 调用名字空间中的类

特性

特性(trait)是为了复用程序代码,在PHP5.4中增加的新功能。PHP不能使用多继承,但是可以将一些常用的功能抽取出来做成trait,然后包含到多个类中,达到代码复用的目的。

特性用关键字trait声明,可以包含属性和方法,但不能包含常量。下面是一个示例:

trait Log {
    public function log(string $message, string $category)
    {
        $line = "[$category]" . date('Y-m-d H:i:s') . " $message";
        file_put_contents('/tmp/log.txt', $line, FILE_APPEDN);
    }
}

class Foo {
    use Log;    // 将特性引入
}

$foo = new Foo();

$foo->log('demo', 'application');    // 调用特性中的方法,行为如同类的成员

参考

  1. http://php.net/
  2. https://itlanyan.com/php-new-features-from-5-2-to-5-6/