PHP

PHP语言基础(二)

Posted by jjx on July 13, 2016

变量

PHP 中的变量用一个美元符号后面跟变量名来表示。变量名是区分大小写的。

变量名与 PHP 中其它的标签一样遵循相同的规则。一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。按照正常的正则表达式,它将被表述为:’[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*‘。

Note: $this 是一个特殊的变量,它不能被赋值。

<?php
$var = 'Bob';
$Var = 'Joe';
echo "$var, $Var";      // 输出 "Bob, Joe"

$4site = 'not yet';     // 非法变量名;以数字开头
$_4site = 'not yet';    // 合法变量名;以下划线开头
$i站点is = 'mansikka';  // 合法变量名;可以用中文
?>

变量默认总是传值赋值。那也就是说,当将一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量。这意味着,例如,当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量。有关这种类型的赋值操作,请参阅表达式一章。

PHP 也提供了另外一种方式给变量赋值:引用赋值。这意味着新的变量简单的引用(换言之,“成为其别名” 或者 “指向”)了原始变量。改动新的变量将影响到原始变量,反之亦然。

使用引用赋值,简单地将一个 & 符号加到将要赋值的变量前(源变量)。例如,下列代码片断将输出“My name is Bob”两次:

<?php
$foo = 'Bob';              // 将 'Bob' 赋给 $foo
$bar = &$foo;              // 通过 $bar 引用 $foo
$bar = "My name is $bar";  // 修改 $bar 变量
echo $bar;
echo $foo;                 // $foo 的值也被修改
?>

有一点重要事项必须指出,那就是只有有名字的变量才可以引用赋值。

虽然在 PHP 中并不需要初始化变量,但对变量进行初始化是个好习惯。未初始化的变量具有其类型的默认值 - 布尔类型的变量默认值是 FALSE,整形和浮点型变量默认值是零,字符串型变量(例如用于 echo 中)默认值是空字符串以及数组变量的默认值是空数组。

依赖未初始化变量的默认值在某些情况下会有问题,例如把一个文件包含到另一个之中时碰上相同的变量名。另外把 register_globals 打开是一个主要的安全隐患。使用未初始化的变量会发出 E_NOTICE 错误,但是在向一个未初始化的数组附加单元时不会。isset() 语言结构可以用来检测一个变量是否已被初始化。

预定义变量

PHP 提供了大量的预定义变量。由于许多变量依赖于运行的服务器的版本和设置,及其它因素,所以并没有详细的说明文档。一些预定义变量在 PHP 以命令行形式运行时并不生效。有关这些变量的详细列表,请参阅预定义变量一章

Warning
PHP 4.2.0 以及后续版本中,PHP 指令 register_globals 的默认值为 off。这是 PHP 的一个主要变化。让 register_globals 的值为 off 将影响到预定义变量集在全局范围内的有效性。例如,为了得到 DOCUMENT_ROOT 的值,将必须使用 $_SERVER[‘DOCUMENT_ROOT’] 代替 $DOCUMENT_ROOT,又如,使用 $_GET[‘id’] 来代替 $id 从 URL http://www.example.com/test.php?id=3 中获取 id 值,亦或使用 $_ENV[‘HOME’] 来代替 $HOME 获取环境变量 HOME 的值。

  • $_POST
  • $_GET
  • $_REQUEST
  • $_SERVER
  • $_FIELS
  • $_SESSION
  • $_COOKIE
  • $_ENV
  • $GLOBALS

关于get和post,如果在get和post同时存在一个变量,应该取哪一个值。

$_REQUST = $_POST + $_GET

如果能够明确是post还是get提交,就直接使用post或者get,当不明确的时候,那就可以使用$_REQUEST。

$_SERVER,和HTTP协议以及服务器相关的一些信息,包括服务器端的IP,浏览器请求端的IP,文件名相对路径等。

使用request时,如果post和get都有一个相同名称的变量时,则只保留post,取决于php的配置 request_order = ‘GP’;

$GLOBALS 引用全局作用域中可用的全部变量

变量范围

变量的范围即它定义的上下文背景(也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。这个单独的范围跨度同样包含了 include 和 require 引入的文件。例如:

<?php
$a = 1;
include 'b.inc';
?>

这里变量 $a 将会在包含文件 b.inc 中生效。但是,在用户自定义函数中,一个局部函数范围将被引入。任何用于函数内部的变量按缺省情况将被限制在局部函数范围内。例如:

<?php
$a = 1; /* global scope */

function Test()
{
    echo $a; /* reference to local scope variable */
}

Test();
?>

这个脚本不会有任何输出,因为 echo 语句引用了一个局部版本的变量 $a,而且在这个范围内,它并没有被赋值。你可能注意到 PHP 的全局变量和 C 语言有一点点不同,在 C 语言中,全局变量在函数中自动生效,除非被局部变量覆盖。这可能引起一些问题,有些人可能不小心就改变了一个全局变量。PHP 中全局变量在函数中使用时必须声明为 global。

global 关键字

<?php
$a = 1;
$b = 2;

function Sum()
{
    global $a, $b;

    $b = $a + $b;
}

Sum();
echo $b;
?>

以上脚本的输出将是“3”。在函数中声明了全局变量 $a 和 $b 之后,对任一变量的所有引用都会指向其全局版本。对于一个函数能够声明的全局变量的最大个数,PHP 没有限制。

在全局范围内访问变量的第二个办法,是用特殊的 PHP 自定义 $GLOBALS 数组。前面的例子可以写成:

<?php
$a = 1;
$b = 2;

function Sum()
{
    $GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}

Sum();
echo $b;
?>

$GLOBALS 是一个关联数组,每一个变量为一个元素,键名对应变量名,值对应变量的内容。$GLOBALS 之所以在全局范围内存在,是因为 $GLOBALS 是一个超全局变量。以下范例显示了超全局变量的用处:

<?php
function test_global()
{
    // 大多数的预定义变量并不 "super",它们需要用 'global' 关键字来使它们在函数的本地区域中有效。
    global $HTTP_POST_VARS;

    echo $HTTP_POST_VARS['name'];

    // Superglobals 在任何范围内都有效,它们并不需要 'global' 声明。Superglobals 是在 PHP 4.1.0 引入的。
    echo $_POST['name'];
}
?>

使用静态变量

变量范围的另一个重要特性是静态变量(static variable)。静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看下面的例子:

<?php
function Test()
{
    $a = 0;
    echo $a;
    $a++;
}
?>

本函数没什么用处,因为每次调用时都会将 $a 的值设为 0 并输出 0。将变量加一的 $a++ 没有作用,因为一旦退出本函数则变量 $a 就不存在了。要写一个不会丢失本次计数值的计数函数,要将变量 $a 定义为静态的:

<?php
function test()
{
    static $a = 0;
    echo $a;
    $a++;
}
?>

现在,变量 $a 仅在第一次调用 test() 函数时被初始化,之后每次调用 test() 函数都会输出 $a 的值并加一。

静态变量也提供了一种处理递归函数的方法。递归函数是一种调用自己的函数。写递归函数时要小心,因为可能会无穷递归下去。必须确保有充分的方法来中止递归。以下这个简单的函数递归计数到 10,使用静态变量 $count 来判断何时停止:

<?php
function test()
{
    static $count = 0;

    $count++;
    echo $count;
    if ($count < 10) {
        test();
    }
    $count--;
}
?>

可变变量

有时候使用可变变量名是很方便的。就是说,一个变量的变量名可以动态的设置和使用。一个普通的变量通过声明来设置,例如:

<?php
$a = 'hello';
?>

一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。在上面的例子中 hello 使用了两个美元符号($)以后,就可以作为一个可变变量的变量了。例如:

<?php
$$a = 'world';
?>

这时,两个变量都被定义了:$a 的内容是“hello”并且 $hello 的内容是“world”。

要将可变变量用于数组,必须解决一个模棱两可的问题。这就是当写下 \(a[1] 时,解析器需要知道是想要 $a[1] 作为一个变量呢,还是想要\)a 作为一个变量并取出该变量中索引为 [1] 的值。解决此问题的语法是,对第一种情况用 ${$a[1]},对第二种情况用 ${$a}[1]。

类的属性也可以通过可变属性名来访问。可变属性名将在该调用所处的范围内被解析。例如,对于 $foo->$bar 表达式,则会在本地范围来解析 $bar 并且其值将被用于 $foo 的属性名。对于 $bar 是数组单元时也是一样。

也可使用花括号来给属性名清晰定界。最有用是在属性位于数组中,或者属性名包含有多个部分或者属性名包含有非法字符时(例如来自 json_decode() 或 SimpleXML)。

来自 PHP 之外的变量

HTML 表单(GET 和 POST)

当一个表单提交给 PHP 脚本时,表单中的信息会自动在脚本中可用。有很多方法访问此信息,例如:

1 一个简单的 HTML 表单

<form action="foo.php" method="POST">
    Name:  <input type="text" name="username"><br />
    Email: <input type="text" name="email"><br />
    <input type="submit" name="submit" value="Submit me!" />
</form>

根据特定的设置和个人的喜好,有很多种方法访问 HTML 表单中的数据。例如:

从一个简单的 POST HTML 表单访问数据

<?php
// 自 PHP 4.1.0 起可用
   echo $_POST['username'];
   echo $_REQUEST['username'];
   
   import_request_variables('p', 'p_');
   echo $p_username;

// 自 PHP 5.0.0 起,这些长格式的预定义变量
// 可用 register_long_arrays 指令关闭。

   echo $HTTP_POST_VARS['username'];

// 如果 PHP 指令 register_globals = on 时可用。不过自
// PHP 4.2.0 起默认值为 register_globals = off。
// 不提倡使用/依赖此种方法。

   echo $username;
?>

HTTP Cookies
PHP 透明地支持 » RFC 6265定义中的 HTTP cookies。Cookies 是一种在远端浏览器端存储数据并能追踪或识别再次访问的用户的机制。可以用 setcookie() 函数设定 cookies。Cookies 是 HTTP 信息头中的一部分,因此 SetCookie 函数必须在向浏览器发送任何输出之前调用。对于 header() 函数也有同样的限制。Cookie 数据会在相应的 cookie 数据数组中可用,例如 $_COOKIE,$HTTP_COOKIE_VARS 和 $_REQUEST。更多细节和例子见 setcookie() 手册页面。

如果要将多个值赋给一个 cookie 变量,必须将其赋成数组。例如:

<?php
  setcookie("MyCookie[foo]", 'Testing 1', time()+3600);
  setcookie("MyCookie[bar]", 'Testing 2', time()+3600);
?>

这将会建立两个单独的 cookie,尽管 MyCookie 在脚本中是一个单一的数组。如果想在仅仅一个 cookie 中设定多个值,考虑先在值上使用 serialize() 或 explode()。

注意在浏览器中一个 cookie 会替换掉上一个同名的 cookie,除非路径或者域不同。因此对于购物车程序可以保留一个计数器并一起传递,例如:

<?php
if (isset($_COOKIE['count'])) {
    $count = $_COOKIE['count'] + 1;
} else {
    $count = 1;
}
setcookie('count', $count, time()+3600);
setcookie("Cart[$count]", $item, time()+3600);
?>

常量

常量是一个简单值的标识符(名字)。如同其名称所暗示的,在脚本执行期间该值不能改变(除了所谓的魔术常量,它们其实不是常量)。常量默认为大小写敏感。传统上常量标识符总是大写的。

常量名和其它任何 PHP 标签遵循同样的命名规则。合法的常量名以字母或下划线开始,后面跟着任何字母,数字或下划线。用正则表达式是这样表达的:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*。

<?php

// 合法的常量名
define("FOO",     "something");
define("FOO2",    "something else");
define("FOO_BAR", "something more");

// 非法的常量名
define("2FOO",    "something");

// 下面的定义是合法的,但应该避免这样做:(自定义常量不要以__开头)
// 也许将来有一天PHP会定义一个__FOO__的魔术常量
// 这样就会与你的代码相冲突
define("__FOO__", "something");

?>

和 superglobals 一样,常量的范围是全局的。不用管作用区域就可以在脚本的任何地方访问常量。有关作用区域的更多信息请阅读手册中的变量范围。

语法

可以用 define() 函数来定义常量,在 PHP 5.3.0 以后,可以使用 const 关键字在类定义之外定义常量。一个常量一旦被定义,就不能再改变或者取消定义。

常量只能包含标量数据(boolean,integer,float 和 string)。可以定义 resource 常量,但应尽量避免,因为会造成不可预料的结果。

可以简单的通过指定其名字来取得常量的值,与变量不同,不应该在常量前面加上 $ 符号。如果常量名是动态的,也可以用函数 constant() 来获取常量的值。用 get_defined_constants() 可以获得所有已定义的常量列表。

Note: 常量和(全局)变量在不同的名字空间中。这意味着例如 TRUE 和 $TRUE 是不同的。

如果使用了一个未定义的常量,PHP 假定想要的是该常量本身的名字,如同用字符串调用它一样(CONSTANT 对应 “CONSTANT”)。此时将发出一个 E_NOTICE 级的错误。参见手册中为什么 $foo[bar] 是错误的(除非事先用 define() 将 bar 定义为一个常量)。如果只想检查是否定义了某常量,用 defined() 函数。

常量和变量有如下不同:

  • 常量前面没有美元符号($);
  • 常量只能用 define() 函数定义,而不能通过赋值语句;
  • 常量可以不用理会变量的作用域而在任何地方定义和访问;
  • 常量一旦定义就不能被重新定义或者取消定义;
  • 常量的值只能是标量。

定义常量

<?php
define("CONSTANT", "Hello world.");
echo CONSTANT; // outputs "Hello world."
echo Constant; // 输出 "Constant" 并发出一个提示级别错误信息
?>

使用关键字 const 定义常量

<?php
// 以下代码在 PHP 5.3.0 后可以正常工作
const CONSTANT = 'Hello World';

echo CONSTANT;
?>

Note:
和使用 define() 来定义常量相反的是,使用 const 关键字定义常量必须处于最顶端的作用区域,因为用此方法是在编译时定义的。这就意味着不能在函数内,循环内以及 if 语句之内用 const 来定义常量。

魔术常量

PHP 向它运行的任何脚本提供了大量的预定义常量。不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了。

有八个魔术常量它们的值随着它们在代码中的位置改变而改变。例如 LINE 的值就依赖于它在脚本中所处的行来决定。这些特殊的常量不区分大小写,如下:

表达式

表达式是 PHP 最重要的基石。在 PHP 中,几乎所写的任何东西都是一个表达式。简单但却最精确的定义一个表达式的方式就是“任何有值的东西”。

最基本的表达式形式是常量和变量。当键入“$a = 5”,即将值“5”分配给变量 $a。“5”,很明显,其值为 5,换句话说“5”是一个值为 5 的表达式(在这里,“5”是一个整型常量)。

赋值之后,所期待情况是 $a 的值为 5,因而如果写下 $b = $a,期望的是它犹如 $b = 5 一样。换句话说,$a 是一个值也为 5 的表达式。如果一切运行正确,那这正是将要发生的正确结果。

稍微复杂的表达式例子就是函数。

PHP 提供了一套完整强大的表达式,而为它提供完整的文件资料已经超出了本手册的范围。上面的例子应该为你提供了一个好的关于什么是表达式和怎样构建一个有用的表达式的概念。在本手册的其余部分,我们将始终使用 expr 来表示一个有效的 PHP 表达式。

运算符

运算符是可以通过给出的一或多个值(用编程行话来说,表达式)来产生另一个值(因而整个结构成为一个表达式)的东西。

运算符可按照其能接受几个值来分组。一元运算符只能接受一个值,例如 !(逻辑取反运算符)或 ++(递增运算符)。 二元运算符可接受两个值,例如熟悉的算术运算符 +(加)和 -(减),大多数 PHP 运算符都是这种。最后是唯一的三元运算符 ? :,可接受三个值;通常就简单称之为“三元运算符”(尽管称之为条件运算符可能更合适)。

PHP 的运算符完整列表见下节运算符优先级。该节也解释了运算符优先级和结合方向,这控制着在表达式包含有若干个不同运算符时究竟怎样对其求值。

  • 运算符优先级
  • 算术运算符
  • 赋值运算符
  • 位运算符
    php的按位或相当于加法 2|3=5
  • 比较运算符
    如果比较一个整数和字符串,则字符串会被转换为整数。如果比较两个数字字符串,则作为整数比较。
    === 和 == 的区别。比如 0 == false 返回真,而0 === false 返回假,因为类型不一样。
  • 错误控制运算符
  • 执行运算符
  • 递增/递减运算符
  • 逻辑运算符
  • 字符串运算符,在PHP中,字符串运算符是.
  • 数组运算符
  • 类型运算符

流程控制

任何 PHP 脚本都是由一系列语句构成的。一条语句可以是一个赋值语句,一个函数调用,一个循环,一个条件语句或者甚至是一个什么也不做的语句(空语句)。语句通常以分号结束。此外,还可以用花括号将一组语句封装成一个语句组。语句组本身可以当作是一行语句。本章介绍了各种语句类型。

通常有三大流程控制:

  • 顺序
  • 分支(选择)
  • 循环

必须要注意的是 elseif 与 else if 只有在类似上例中使用花括号的情况下才认为是完全相同。如果用冒号来定义 if/elseif 条件,那就不能用两个单词的 else if,否则 PHP 会产生解析错误。

<?php

/* 不正确的使用方法: */
if($a > $b):
    echo $a." is greater than ".$b;
else if($a == $b): // 将无法编译
    echo "The above line causes a parse error.";
endif;


/* 正确的使用方法: */
if($a > $b):
    echo $a." is greater than ".$b;
elseif($a == $b): // 注意使用了一个单词的 elseif
    echo $a." equals ".$b;
else:
    echo $a." is neither greater than or equal to ".$b;
endif;

?>

函数

返回值

值通过使用可选的返回语句返回。可以返回包括数组和对象的任意类型。返回语句会立即中止函数的运行,并且将控制权交回调用该函数的代码行。更多信息见 return

可变函数

PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。

可变函数不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。

匿名函数

匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。

<?php
$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?>