#7 PHP 函数的引用返回

2011-12-07

遇到函数声明时前面加引用符号的情况。

调用函数时加引用符号可以理解,就是声明的接受变量是一个引用嘛。(实验证明,这是错的,说引用类型只能赋值给变量。What?不知所云,求指教。)

可是函数声明就没有那么好理解了。

php.net 中的例子:

<?php
class foo {
    public $value = 42;
    public function &getValue() {
        return $this->value;
    }
}
$obj = new foo;
$myValue = &$obj->getValue(); // $myValue is a reference to $obj->value, which is 42.
$obj->value = 2;
echo $myValue; // prints the new value of $obj->value, i.e. 2.
?>

本例中 getValue 函数所返回的对象的属性将被赋值,而不是拷贝,就和没有用引用语法一样。

还是没太明白,试验先。

eg 1、普通的函数声明,然后引用接收。

function foo() {
    $var = 0;
    return $var;
}
$get = & foo();
var_dump($get);
$get = 5;
$get = & foo();
var_dump($get);

结果:

Strict standards: Only variables should be assigned by reference
int 0
Strict standards: Only variables should be assigned by reference
int 0

eg 2、将上面例子中的函数声明成了引用返回。

function & foo() {
    $var = 0;
    return $var;
}
$get = foo();
var_dump($get);
$get = 5;
$get = foo();
var_dump($get);

结果:

int 0
int 0

和没有把函数声明成引用一样的效果。

eg 3、将上面例子中的函数声明成了引用返回。

function & foo() {
    static $var = 0;
    return $var;
}
$get = & foo();
var_dump($get);
$get = 5;
$get = & foo();
var_dump($get);

结果:

int 0

int 5

eg 4、面向对象中引用返回的应用。

/**
* 试验辅类,用于试验主类属性 ref 的 `->` 操作。
*/
class Ex {
public $some;
    function __construct() {
        $this->some = 0;
    }
    public function set($v) {
        $this->some = $v;
    }
    public function get() {
        return $this->some;
    }
}
/**
* 试验主类
*/
class MyClass {
    public $ref;
    function __construct() {
        $this->ref = & foo();
    }
}
function & foo() {
    $var = new Ex;
    return $var;
}
$class = new MyClass();
$class->ref->set(5);
var_dump($class->ref->get());

结果:

int 5

总结

  1. 一般来说,函数返回就是返回一个值。
  2. 引用就是计算出变量的地址。
  3. 引用返回是把返回变量的引用返回来了。而且,返回引用的格式决定了两边都要用 & 声明。
    若只是在函数那边声明引用,那么返回结果将依然是赋值给接收变量。是不是就是返回一个变量拷贝呢?
    若只是在接收变量那边声明引用就会报错,因为函数(还是返回的值?)不能用 & 求地址。

#6 魔术常量 & 魔术方法 & 魔术引号

2011-11-10

魔术常量

https://php.net/manual/zh/language.constants.predefined.php

  • __LINE__ 文件中的当前行号。
  • __FILE__ 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。
  • __DIR__ 文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。它等价于 dirname(FILE)。除非是根目录,否则目录中名不包括末尾的斜杠。(PHP 5.3.0中新增)
  • __FUNCTION__ 函数名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该函数被定义时的名字(区分大小写)。
  • __CLASS__ 类的名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该类被定义时的名字(区分大小写)。
  • __METHOD__ 类的方法名(PHP 5.0.0 新加)。返回该方法被定义时的名字(区分大小写)。
  • __NAMESPACE__ 当前命名空间的名称(大小写敏感)。这个常量是在编译时定义的(PHP 5.3.0 新增)

魔术方法

https://php.net/manual/zh/language.oop5.magic.php

约十三个有特殊含义的方法:
__construct()__destruct()__call()__callStatic()__get()
__set()__isset()__unset()__sleep()__wakeup()__toString()
__invoke()__set_state()__clone()

这些方法在PHP中被称为"魔术方法"(Magic methods)。 你在命名自己的类方法时不能使用这些方法名, 除非你希望使用"魔术"功能。

构造函数和析构函数

如果你想明确地销毁一个对象,你可以给指向该对象的变量分配任何其它值.通常将变量赋值勤为NULL或者调用unset。

属性重载

  • public void __set ( string $name , mixed $value ) 在给未定义的变量赋值时调用。
  • public mixed __get ( string $name ) 读取未定义的变量的值时调用。
  • public bool __isset ( string $name ) 当对未定义的变量调用 isset()empty() 时调用。
  • public void __unset ( string $name ) 当对未定义的变量调用 unset() 时调用。

参数 $name 是指要操作的变量名称 ,包括没有访问权限的属性(protected,private)。__set() 方法的 $value 参数指定了 $name 变量的值。
属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被 声明为static。 从PHP 5.3.0起, 将这些魔术方法定义为static会产生一个警告。

方法重载

  • public mixed __call ( string $name , array $arguments ) 当调用一个不可访问方法(如未定义,或者不可见)时调用 。
  • public static mixed __callStatic ( string $name , array $arguments ) 当在静态方法中调用一个不可访问方法(如未定义,或者不可见)时调用。
  • $name 参数是要调用的方法名称。
  • $arguments 参数是一个数组,包含着要传递给方法的参数。

__sleep()__wakeup()

public array __sleep ( void )

函数会检查是否存在一个魔术方法 __sleep().
如果存在,__sleep() 方法会先被调用,然后才执行序列化操作。
这个功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
如果该方法不返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 错误。

__sleep()方法常用于提交未提交的数据,或类似的清理操作。同时,如果你有一些很大的对象, 不需要全部保存,这个功能就很好用。

void __wakeup ( void )serialize()

与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
__wakeup()经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。

对象的输出

public string __toString ( void )

当对象被当做字符串输出时这个函数会被调用,该方法必须返回一个字符串,否则产生一个 E_RECOVERABLE_ERROR 致命错误。

mixed __invoke ([ $... ] )

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

static object __set_state ( array $properties )

当调用 var_export() 时,这个静态方法会被调用(自PHP 5.1.0起有效)。
本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性。

对象复制

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同, 但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况: 如果对象A中保存着对象B的引用,当你复制对象A时,你想其中使用的对象不再是对象B而是B的一个副本,那么 你必须得到对象A的一个副本。

对象复制可以通过clone关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。

$copy_of_object = clone $object;

当对象被复制后,PHP5会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。

void __clone ( void )

当复制完成时, 如果定义了 __clone() 方法, 则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用, 可用于修改属性的值(如果有必要的话)。

自动加载对象

很多开发者写面向对象的应用程序时对每个类的定义建立一个 PHP 源文件。一个很大的烦恼是不得不在每个脚本(每个类一个文件)开头写一个长长的包含文件列表。在 PHP 5 中,不再需要这样了。可以定义一个 __autoload 函数,它会在试图使用尚未被定义的类时自动调用。通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。

Note:在 __autoload 函数中抛出的异常不能被 catch 语句块捕获并导致致命错误,而且如果使用 PHP 的 CLI 交互模式 时,Autoloading 不存在。

魔术引号(废弃)

魔术引号(Magic Quote)是一个自动将进入 PHP 脚本的数据进行转义的过程。最好在编码时不要转义而在运行时根据需要而转义。 magic_quotes_gpc 影响到 HTTP 请求数据(GETPOSTCOOKIE)。不能在运行时改变。在 PHP 中默认值为 on。

  • magic_quotes_runtime 如果打开的话,大部份从外部来源取得数据并返回的函数,包括从数据库和文本文件,所返回的数据都会被反斜线转义。
    该选项可在运行的时改变,在 PHP 中的默认值为 off。
  • magic_guotes_sybase 如果打开的话,将会使用单引号对单引号进行转义而非反斜线。
    此选项会完全覆盖 magic_quotes_gpc 。如果同时打开两个选项的话,单引号将会被转义成 ''。而双引号、反斜线 和 NULL 字符将不会进行转义。
__autoload

如果要定义一个全局的自动加载类,则必须用 spl_autoload_register() 方法将处理类注册到PHP标准库:

<?php
    class Loader    {
        static function autoload_class($class_name)   {
            //寻找正确的$class_name类,并引入,没有则抛出异常
        }
    }
    /**
    *   设置对象的自动载入
    *   spl`_autoload_register — Register given function as __autoload()` implementation
    */
    spl_autoload_register(array('Loader', 'autoload_class'));
    $a = new Test();//Test没用require就实例化,实现自动加载,很多框架就用这种方法自动加载类
?>

注意: 在 __autoload 函数中抛出的异常不能被 catch 语句块捕获并导致致命错误,所以应该在函数本身做捕获。

#5 Ajax

2011-05-12

Ajax 是一种让网页“不用刷新页面,也能和服务器通信”的技术。

比如:

  • 搜索框自动提示
  • 点赞后数字立即变化
  • 提交表单后局部刷新

这些功能背后通常都用了 Ajax。

什么是 Ajax?

Ajax 全称 Asynchronous JavaScript and XML,意思是 JS 异步请求服务器数据,虽然名字里有 XML,但实际开发里更多使用 JSON。

传统网页的工作流程:

  1. 点击按钮
  2. 浏览器请求服务器
  3. 服务器返回完整页面
  4. 浏览器整体刷新

问题:

  • 页面会闪一下
  • 速度较慢
  • 用户体验不好
  • 浪费带宽

Ajax 出现后:

  • 页面无需整体刷新
  • 后台获取数据
  • 只更新局部区域
  • 交互体验更流畅

Ajax 的实现方式

  1. 创建 XMLHttpRequest 对象
  2. 配置请求
  3. 发送请求
  4. 等待服务器响应
  5. 更新页面内容

GET 获取数据

function loadUser() {
  // 创建 XMLHttpRequest 对象
  var xhr = new XMLHttpRequest();

  // 配置请求
  xhr.open("GET", "/api/user", true);

  // 监听状态变化
  xhr.onreadystatechange = function () {
    // 请求完成
    if (xhr.readyState == 4) {
      // 请求成功
      if (xhr.status == 200) {
        // 输出服务器返回的数据
        console.log(xhr.responseText);
      } else {
        console.log("请求失败");
      }
    }
  };

  // 发送请求
  xhr.send();
}

POST 提交数据

function submitData() {
  var xhr = new XMLHttpRequest();

  xhr.open("POST", "/api/user", true);

  // 设置请求头
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4 && xhr.status == 200) {
      console.log("提交成功");
    }
  };

  // 提交数据
  xhr.send("name=Tom&age=18");
}

IE 浏览器兼容

IE6/IE7 需要使用:

ActiveXObject;

#4 PHP 文件操作

2011-05-03
// 创建目录
if (!is_dir("mydirectory")) {
    mkdir("mydirectory");
} else {
    die("目录已存在");
}
// 创建多级目录
mkdir("path/to/my/directory", 0777, true);

// 删除目录
rmdir("newdir");

// 打开目录
$dir = opendir(".");

// 读目录
while($file = readdir($dir)) {
    echo $file . "<br />";
}

// 读目录 2
$directory = '/path/to/dir';
$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($directory,
        RecursiveDirectoryIterator::SKIP_DOTS), // 忽略 . / ..
    RecursiveIteratorIterator::SELF_FIRST); // 深度优先
foreach ($iterator as $item) {
    if ($item->isDir()) {
        echo "目录: " . $item->getPath() . "\n";
    } else {
        echo "文件: " . $item->getPathname() . "\n";
    }
}

// 关闭目录
closedir($dir);

// 打开文件
$myfile = fopen("example.txt", "r");

// 读文件
$data = fread($myfile, filesize("example.txt"));

// 写文件
fwrite($myfile, "This is some text");

fclose($myfile);

// 读文件
$data = file_get_contents("example.txt");

// 写文件
file_put_contents("example.txt", "This is some text");

// 删除文件
unlink("example.txt");

#3 PHP 时间处理

2011-05-02

date/time/strtotime

time()  # 获得当前时间戳
date(fmtStr, timestamp)

strtotime("+1 day")
strtotime("next Thursday")
strtotime("last Thursday")
# 字符串 => 时间戳
php -r 'var_dump(strtotime("today"));' # 零点
php -r 'var_dump(strtotime("2011"));'
php -r 'var_dump(strtotime("2011-12"));'
php -r 'var_dump(strtotime("2011-12-13"));'
php -r 'var_dump(strtotime("2011-12-13 06:00:00"));'

# 时间戳 => 字符串
php -r 'var_dump(date("Y-m-d H:i:s", 0));'

DateTime

  • DateTimeInterface
  • DateTime
  • DateTimeImmutable
  • DateTimeZone
  • DateInterval
  • DatePeriod 实现 Traversable 接口
// DateTimeInterface::ATOM = "Y-m-d\TH:i:sP";
// DateTimeInterface::COOKIE = "l, d-M-Y H:i:s T";
// DateTimeInterface::ISO8601 = "Y-m-d\TH:i:sO";
// DateTimeInterface::RFC822 = "D, d M y H:i:s O";
// DateTimeInterface::RFC850 = "l, d-M-y H:i:s T";
// DateTimeInterface::RFC1036 = "D, d M y H:i:s O";
// DateTimeInterface::RFC1123 = "D, d M Y H:i:s O";
// DateTimeInterface::RFC7231 = "D, d M Y H:i:s \G\M\T";
// DateTimeInterface::RFC2822 = "D, d M Y H:i:s O";
// DateTimeInterface::RFC3339 = "Y-m-d\TH:i:sP";
// DateTimeInterface::RFC3339_EXTENDED = "Y-m-d\TH:i:s.vP";
// DateTimeInterface::RSS = "D, d M Y H:i:s O";
// DateTimeInterface::W3C = "Y-m-d\TH:i:sP";

$dateTime = new DateTime();

foreach ([
    'ATOM',
    'COOKIE',
    'ISO8601',
    'RFC822',
    'RFC850',
    'RFC1036',
    'RFC1123',
    'RFC2822',
    'RFC3339',
    'RFC3339_EXTENDED',
    'RSS',
    'W3C',
] as $format) {
    eval("print 'DateTimeInterface::$format\t'.\$dateTime->format(DateTimeInterface::$format).\"\n\";");
}
  • public static createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false

  • public format(string $format): string

  • public modify(string $modifier): DateTime|false

  • public add(DateInterval $interval): DateTime
  • public sub(DateInterval $interval): DateTime

  • public diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval

  • public getOffset(): int

  • public getTimestamp(): int
  • public setTimestamp(int $timestamp): DateTime
  • public getTimezone(): DateTimeZone|false
  • public setTimezone(DateTimeZone $timezone): DateTime
  • public setDate(int $year, int $month, int $day): DateTime
  • public setISODate(int $year, int $week, int $dayOfWeek = 1): DateTime
  • public setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime
$d = new DateTime('2011-01-01T15:03:01.012345Z');
echo $d->format('Y-m-d\TH:i:s.u'); // 2011-01-01T15:03:01.012345

$d->modify('+1 month');
$d->add(new DateInterval("P1M"));

$publishDate = DateTime::createFromFormat('m/d/Y', '1/10/2014');
echo $publishDate->getTimestamp();

#1 关于 PHP

2011-03-06

PHP 是互联网领域最主流的后端语言之一,主要用于 Web 开发,优点就是学习门槛低,部署也简单。
因此大量网站都基于 PHP 构建,包括:

  • 博客
  • 论坛
  • CMS
  • 商城
  • 企业官网

典型代表:

  • WordPress
  • Discuz!
  • Drupal

PHP 的本质

“嵌入 HTML 的服务器端脚本语言”

服务器收到请求后:

浏览器请求
    ↓
Web 服务器(Apache)
    ↓
PHP 解释执行
    ↓
生成 HTML
    ↓
返回浏览器

浏览器永远看不到 PHP 源码,只能看到 PHP 执行后的结果。

PHP 文件

  1. PHP 文件通常以 .php 结尾。
  2. PHP 代码写在 php 标签中,例如:

    <?php
    echo "Hello PHP";
    ?>
    

PHP 最核心的特点

  1. 解释执行,不需要编译。
  2. 面向 Web,内置大量 Web 能力:

  3. 表单处理

  4. Cookie
  5. Session
  6. 文件上传
  7. HTTP Header
  8. 数据库访问

  9. HTML 混编,例如:

    <html>
    <body>
    
    <h1>用户列表</h1>
    
    <?php
    echo "<p>Tom</p>";
    echo "<p>Jerry</p>";
    ?>
    
    </body>
    </html>
    

PHP 基础语法

1. 输出

<?php
echo "Hello";
print "World";
?>

2. 变量

PHP 变量以 $ 开头。

<?php

$name = "Tom";
$age = 18;

echo $name;
echo $age;

?>

特点:

  • 不需要声明类型
  • 自动推断类型

3. 数据类型

常见类型:

$string = "abc";
$int = 123;
$float = 3.14;
$bool = true;
$array = array();

PHP 属于弱类型语言。

数组(非常重要)

PHP 的数组功能极强,可以实现列表、字典(HashMap)。

<?php

# 普通数组
$names = array("Tom", "Jerry");
echo $names[0];

# 关联数组
$user = array(
    "name" => "Tom",
    "age" => 18
);
echo $user["name"];
?>

流程控制

if 判断

<?php

$age = 20;

if ($age >= 18) {
    echo "成人";
} else {
    echo "未成年";
}

?>

for 循环

<?php

for ($i = 0; $i < 5; $i++) {
    echo $i;
}

?>

foreach(高频)

<?php

$names = array("Tom", "Jerry");

foreach ($names as $name) {
    echo $name;
}

?>

PHP 中大量数据遍历都依赖 foreach

函数

定义函数:

<?php

function add($a, $b) {
    return $a + $b;
}

echo add(1, 2);

?>

特点:

  • 参数无需声明类型
  • 返回值无需声明类型

表单处理

<form method="post">
  <input name="username" />
  <input type="submit" />
</form>
<?php

$username = $_POST['username'];

echo $username;

?>

PHP 提供超级全局变量:

  • $_GET
  • $_POST
  • $_COOKIE
  • $_SESSION
  • $_FILES

这是 Web 编程基础。

数据库操作(MySQL)

<?php

mysql_connect("localhost", "root", "123456");

$result = mysql_query("SELECT * FROM users");

?>

mysqli
pdo

Session 与 Cookie

Cookie 在浏览器中保存会话数据。

<?php

setcookie("username", "Tom");

?>

Session 在服务器保存会话数据。

<?php

session_start();

$_SESSION['user_id'] = 1;

?>

登录系统大量依赖 Session。

PHP 的运行环境

Linux 下的 LAMP 组合:Linux + Apache + MySQL + PHP
Windows 下反正我都是选择 XAMPP 套件,也是包含 Apache + MySQL + PHP。