Jinyun's Notes 🚀

没什么天赋,爱好也不多,但愿坚持做些喜欢的事情

0%

PHP 的奇技淫巧

201912031234.png

🍓 每当看到 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 中的 call 和 apply 差求不多,另外 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());

持续更新中…

本笔记是笔者在学习和工作中的一些整理,如对您有用,请鼓励我继续写作