目录

PHP 中的一些巧妙写法

每当看到 PHP 写的很冗长奇臭的代码亦或片段,我就有一股将它斩成几节的冲动,无奈中自有一种拔剑四顾心茫然之感(由于不同的 PHP 版本,加上语法的限制)。我在开发工具上是一个比较挑剔的人,在 macOS 也花了不少钱购置工具,每次看到其它人为了破解工具折腾的死去活来痛不欲生时,我巴不得他们多折腾一会儿。

更加优雅的判断语句

isset 语句

isset 参与判断赋值

1
2
3
4
5
6
7
8
<?php

$array = $_REQUEST;
$result = '';

if (is_array($array) && isset($array['key'])) {
    $result = $array['key'];
}

改写为

1
2
3
4
<?php

$array = (array) $_REQUEST;
$result = $array['key'] ?? '';

isset 参与并且的多个判断

1
2
3
4
5
6
7
8
<?php

$array = (array) $_REQUEST;
$result = false;

if (isset($array['a']) && isset($array['b']) && isset($array['c'])) {
    $result = true;
}

改写为

1
2
3
4
<?php

$array = (array) $_REQUEST;
$result = isset($array['a'], $array['b'], $array['c']);

isset 参与或者的多个判断

1
2
3
4
5
6
7
8
<?php

$array = (array) $_REQUEST;
$result = false;

if (isset($array['a']) || isset($array['b']) || isset($array['c'])) {
    $result = true;
}

改写为

1
2
3
4
<?php

$array = (array) $_REQUEST;
$result = isset($array['a']) || isset($array['b']) || isset($array['c']);

empty 语句

empty 参与判断赋值

1
2
3
4
5
6
7
8
<?php

$array = ['a' => 0, 'b' => false, 'c' => '0'];
$result = 'oldValue';

if (! empty($array['a']) && ! empty($array['b']) && ! empty($array['c'])) {
    $result = 'newValue';
}

改写为

1
2
3
4
<?php

$array = ['a' => 0, 'b' => false, 'c' => '0'];
$result = $array['a'] ?: $array['b'] ?: $array['c'] ?: 'oldValue';

empty 参与并且的多个判断

1
2
3
4
5
6
7
8
<?php

$array = ['a' => 1, 'b' => true, 'c' => 'null'];
$result = false;

if (! empty($array['a']) && ! empty($array['b']) && ! empty($array['c'])) {
    $result = true;
}

改写为

1
2
3
4
<?php

$array = ['a' => 1, 'b' => true, 'c' => 'null'];
$result = ! empty($array['a']) && ! empty($array['b']) && ! empty($array['c']);

empty 参与或者的多个判断

1
2
3
4
5
6
7
8
<?php

$array = ['a' => 1, 'b' => true, 'c' => 'null'];
$result = false;

if (! empty($array['a']) || ! empty($array['b']) || ! empty($array['c'])) {
    $result = true;
}

改写为

1
2
3
4
<?php

$array = ['a' => 1, 'b' => true, 'c' => 'null'];
$result = ! empty($array['a']) || ! empty($array['b']) || ! empty($array['c']);

判断大小语句

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

function compare(int $a, int $b): int
{
    if ($a === $b) {
        return 0;
    }

    return $a > $b ? 1 : -1;
}

改写为

1
2
3
4
5
6
<?php

function compare(int $a, int $b): int
{
    return $a <=> $b;
}

Null 合并运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

$array = ['a' => 0, 'b' => false, 'c' => null, 'd'];

if (is_null($array['c'])) {
    $array['c'] = 'c is null';
}

if ($array['d'] === null) {
    $array['d'] = 'd is null';
}

改写为

1
2
3
4
5
<?php

$array = ['a' => 0, 'b' => false, 'c' => null, 'd'];
$array['c'] ??= 'c is null';
$array['d'] ??= 'd is null';

方法参数变短

方法参数变短的风险时增加了调用的难度,所以注释就很必要了。不过带来的好处时,方法的复用性远甚固定参数。尤其适合重构的场景,比如:之前的方法(N 多的地方调用这个方法)多加一个参数 就可以处理 现在的逻辑 的场景,我想你一定会遇到过,如果没有遇到过,别着急,迟早的事。

1
2
3
4
5
6
<?php

function test(string $a, string $b, string $c, string $d, string $e, array $f, bool $g = false)
{
    var_dump($a, $b, $c, $d, $e, $f, $e);
}

改写为

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function test(...$args)
{
    var_dump($args);
}

// 或者

function test()
{
    var_dump(func_get_args());
}

更加优雅的预定义变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php

function test(array $array, bool $printed = false): array
{
    $a = '';
    $b = [];
    $c = false;
    $d = 0;
    $e = new \stdClass();

    // TODO: 处理...

    $result = doSomething();

    return $result ?: [];
}

改写为

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php

function test(array $array, bool $printed = false): array
{
    [$a, $b, $c, $d, $e] = ['', [], false, 0, new \stdClass()];

    // TODO: 处理...

    $result = doSomething();

    return $result ?: [];
}

使用标准类库

PHP标准库 (SPL)

SPL,即 PHP 标准库(Standard PHP Library),从 PHP 5.0 起内置的组件和接口,并且从 PHP5.3 已逐渐的成熟。SPL 其实在所有的 PHP5 开发环境中被内置,同时无需任何设置。然而 SPL 了似乎被我们无视了,我们总是喜欢造一些不靠谱的轮子也不愿意花时间去学习一些成熟的解决方案。通过 SPL 工具集合我们就可以轻松组装一把瑞士军刀,当然,我这么说可能有些苍白无力,如果你刷过算法和数据结构,你一定懂 SPL 的匠心。

实例之文件信息类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

$filename = __DIR__ . '/a.txt';
$info = new SplFileInfo($filename);
var_dump($info->getBasename('.txt'));
var_dump($info->getExtension());
var_dump($info->getFilename());
var_dump($info->getGroup());
var_dump(date('Y-m-d H:i:s', $info->getCTime()));
var_dump(date('Y-m-d H:i:s', $info->getATime()));
var_dump($info->isDir());
var_dump($info->isFile());
var_dump($info->isReadable());
var_dump($info->isWritable());
var_dump($info->isExecutable());
var_dump($info->getOwner());

// 结果
string(1) "a"
string(3) "txt"
string(5) "a.txt"
int(20)
string(19) "2019-12-03 03:18:21"
string(19) "2019-12-03 03:18:22"
bool(false)
bool(true)
bool(true)
bool(true)
bool(false)
int(501)

实例之优先级队列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

$queue = new SplPriorityQueue();
$queue->insert('A', 3);
$queue->insert('B', 6);
$queue->insert('C', 1);
$queue->insert('D', 2);
$queue->insert('E', 5);
$queue->insert('F', 4);

var_dump($queue->count());

while ($queue->valid()) {
    echo $queue->current(), ' ';
    $queue->next();
}
echo PHP_EOL;

var_dump($queue->compare('A', 'F'));

// 结果
int(6)
B E F A D C
int(-1)

其它

交换两个变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

$a = 1;
$b = 2;
[$b, $a] = [$a, $b];
var_dump($a, $b);

// 结果
int(2)
int(1)

或者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php

$a = 1;
$b = 2;

$a ^= $b;
$b ^= $a;
$a ^= $b;

var_dump($a, $b);

// 结果
int(2)
int(1)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php

$a = 1;
$b = 2;

$tmp = $a;
$a = $b;
$b = $tmp;

var_dump($a, $b);

// 结果
int(2)
int(1)

定义函数并立即执行

1
2
3
4
5
<?php

call_user_func(static function () {
    echo 'Hello World!';
});

闭包当成对象的成员方法或者静态成员方法

关于 bind 和 bindTo 的官网文档解释有点绕,其实它们的功能与 JS 中的 callapply 差求不多,另外 JavaScript 中 apply 、call 的详解

实例一

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

class Test
{
    private function doSomething(array $array): array
    {
        return array_map('strtoupper', $array);
    }
}

$test = new Test();
$func = function (array $array) {
    return $this->doSomething($array);
};
$result = $func->bindTo($test, $test)(['a', 'b', 'c', 'd']);
var_dump($result);

// 结果
array(4) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
  [3]=>
  string(1) "D"
}

实例二

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php

trait DynamicDefinition
{
    public function __call(string $name, $args = null)
    {
        if (is_callable($this->{$name})) {
            return call_user_func($this->{$name}, $args);
        }

        throw new \RuntimeException("Method {$name} does not exist");
    }

    public function __set(string $name, $value = null)
    {
        $this->{$name} = is_callable($value) ? $value->bindTo($this, $this) : $value;
    }
}

class Foo
{
    use DynamicDefinition;

    private string $privateValue = 'I am private';
}

$foo = new Foo;
$foo->bar = function (...$arguments) {
    $args = $arguments[0];
    $prefix = implode('➣', $args);

    return $prefix . ' ' . $this->privateValue;
};
print $foo->bar('🙏', '🧨', '🎉');


// 结果
🙏➣🧨➣🎉 I am private

实例三

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php

class Foo
{
    private static int $a = 11;
    private int $b = 22;
}

$aFunc = static function () {
    return Foo::$a;
};

$bFunc = function () {
    return $this->b;
};

$aResult = Closure::bind($aFunc, null, Foo::class);
$bResult = Closure::bind($bFunc, new Foo(), Foo::class);
var_dump($aResult(), $bResult());