PHP 拾遗


PHP 拾遗


json_encode

原来只知道json_encode()可以把数组格式化为json格式,原来json_encode()还可以把字符串格式化为json格式, 不过对该格式化后的json对象,使用json_decode($json)后还是字符串,而不是数组。

如:

<?php
$string = '{"name":"John", "age":30, "city":"New York"}';
$jsonArray = json_encode($string);

echo $jsonArray;  // 输出: "{\"name\":\"John\", \"age\":30, \"city\":\"New York\"}" 

echo json_decode($jsonArray, true);  // 输出:{"name":"John", "age":30, "city":"New York"} 

常规数组操作:

<?php
$arr = ['name' => 'John', 'age' => 30, 'city' => 'New York'];
$str = json_encode($arr);
echo $str;  // {"name":"John","age":30,"city":"New York"}

$str2 = '{"name":"John","age":30,"city":"New York"}';
$arr2 = json_decode($str2, true);
print_r($arr2);
// 输出
Array
(
    [name] => John
    [age] => 30
    [city] => New York
)

字符串拼接:

<?php
$str = '{' .'"name":"' .'John' .'","age":' .'30' .',"city":"' .'New York' .'"' .'}';
$arr = json_decode($str, true);
print_r($arr);
// 输出
Array
(
    [name] => John
    [age] => 30
    [city] => New York
)

参考地址 https://www.php.net/manual/zh/function.json-encode.php

函数原型: json_encode(mixed $value, int $flags = 0, int $depth = 512): string|false

$flags 参数有2个比较常用到的参数值:

  • JSON_UNESCAPED_UNICODE(中文不转为unicode ,对应的数字 256)
  • JSON_UNESCAPED_SLASHES (不转义反斜杠,对应的数字 64)

通常json_encode只能传入一个常量,如果同时使用2个常量怎么办?

JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES = 320

使用方法:json_encode($arr, 320),即可完成同时使用2个常量,会保留 / 和 UNICODE字符(如:中文)。

如下:

<?php
$arr = ['/ a 你好'];

echo json_encode($arr);  // ["\/ a \u4f60\u597d"]

echo json_encode($arr, 320);  // ["/ a 你好"]

array_map

array_map() 可以循环对数组内每个元素进行操作。

array_map()函数:多数组回调函数,将回调函数作用到给定数组的单元上

  1. 语法:array array_map ( callback callback, array arr1 [, array ...] )
  2. 描述:返回一个数组,该数组包含了 arr1 中的所有单元经过 callback 作用过之后的单元。 callback 接受的参数数目应该和传递给 array_map() 函数的数组数目一致。
  3. 注意事项1:多数组回调函数作用于一个数组时,将保留原有数组的键名,也就是返回的数组的键名就是作用到给定数组的键名
  4. 注意事项2:多数组回调函数作用于两个或多个数组时,他们的长度要一致,并且将忽略原来多个数组的键名,统一分配数字索引作为键名
 <?php
//单个数组使用的例子 
$websites = array("g" => "google", "b" => "baidu", "y" => "yahoo");
//输出原数组 
echo "<pre>";
print_r($websites);
echo "</pre>";
//定义对单个数组处理的回调函数 
function change_value($value)
{
    return ucfirst($value) . ".com";
}

$urls = array_map('change_value', $websites);
echo "<pre>";
print_r($urls);
echo "</pre>";
//多个数组使用的例子 
$arr1 = array(1, 3, 5, 7);
$arr2 = array(2, 4, 6, 8);
//定义对多个数组处理的回调函数 
function func1($a, $b)
{
    return $a * $b;
}

$results = array_map('func1', $arr1, $arr2);
echo "利用回调函数对多个数组处理后,返回的结果:<br>";
echo "<pre>";
print_r($results);
echo "</pre>"; 

输出:

Array
(
    [g] => google
    [b] => baidu
    [y] => yahoo
)

Array
(
    [g] => Google.com
    [b] => Baidu.com
    [y] => Yahoo.com
)

利用回调函数对多个数组处理后,返回的结果:

Array
(
    [0] => 2
    [1] => 12
    [2] => 30
    [3] => 56
)

对返回的元素可以进行合并,如$string = implode(",", array_map($function, $array))

array_map原型:

/**
 * Applies the callback to the elements of the given arrays
 * @link http://php.net/manual/en/function.array-map.php
 * @param callback $callback <p>
 * Callback function to run for each element in each array.
 * </p>
 * @param array $arr1 <p>
 * An array to run through the callback function.
 * </p>
 * @param array $_ [optional]
 * @return array an array containing all the elements of arr1
 * after applying the callback function to each one.
 * @since 4.0.6
 * @since 5.0
 */
function array_map($callback, array $arr1, array $_ = null) { }

call_user_func

call_user_func — 把第一个参数作为回调函数调用,原型:

call_user_func(callable $callback, mixed ...$args): mixed

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

callback 可以是一个闭包函数、或者单个函数的函数名,也可以是一个类的静态函数、或者public函数。

看个例子:

<?php
class myclass {
    public static function say_hello($data = '')
    {
        echo "Hello: $data!\n";
    }
	
	public function say_world($data = '')
    {
        echo "World: $data!\n";
    }
}

$classname = "myclass";
call_user_func(array($classname, 'say_hello'), 'tom');
call_user_func($classname .'::say_hello', 'cand');
call_user_func(array($classname, 'say_world'), 'sala');
call_user_func($classname .'::say_world', 'yeal');

$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'), 'zhangsan');
call_user_func(array($classname, 'say_world'), 'lisi');

$classname = "myclass";
$actionname = "say_world";
call_user_func(array($classname, $actionname), 'adany');
call_user_func($classname .'::' .$actionname, 'jake');

// 输出
// Hello: tom!
// Hello: cand!
// World: sala!
// World: yeal!

// Hello: zhangsan!
// World: lisi!

// World: adany!
// World: jake!

因为这个函数特殊的用法,灵活使用,可以在项目中起到依赖注入的作用。

与这个函数相关内容有:

  • call_user_func_array() - 调用回调函数,并把一个数组参数作为回调函数的参数
  • is_callable() - 验证值是否可以在当前范围内作为函数调用。
  • Variable functions 可变函数。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可
  • ReflectionFunction::invoke() - Invokes function
  • ReflectionMethod::invoke() - Invoke

函数参数…

在函数function sum(...$numbers) { }中参数...$numbers是什么,怎么用?

在 PHP 中,函数参数中的 ... 是用来表示函数可以接受可变数量的参数的。这被称为“参数展开”或“可变参数列表”。

当你看到 function sum(...$numbers) { } 这样的代码时,...$numbers 表示将数组 $numbers 中的所有元素作为单独的参数传递给函数 sum。 这里,...$numbers 必须是一个数组或实现了 __invoke 方法的对象。

举个例子,假设你有一个函数 sum,它可以接受任意数量的参数,并返回它们的和:

<?php
function sum(...$numbers) {  
    $total = 0;  
    foreach ($numbers as $number) {  
        $total += $number;  
    }  
    return $total;  
}  
  
// 使用一个数组作为参数  
$numbersArray = [1, 2, 3, 4];  
echo sum(...$numbersArray); // 输出 10  
  
// 直接传递参数  
echo sum(1, 2, 3, 4); // 输出 10

在上面的例子中,...$numbers 表示函数 sum 可以接受任意数量的参数,并将它们存储在数组 $numbers 中。然后,我们遍历这个数组来计算总和。

如果你有一个数组 $numbersArray,你可以使用 ...$numbersArray 来将数组中的每个元素作为单独的参数传递给 sum 函数。

另外,你也可以将对象作为可变参数传递,只要这个对象实现了 __invoke 方法。当对象被当作函数来调用时,__invoke 方法会被执行。

总的来说,... 在函数参数中的用法允许你以灵活的方式传递参数,包括数组中的元素或对象的方法调用。

usort

参考 https://www.php.net/manual/zh/function.usort

usort() ,使用用户自定义的比较函数对数组中的值进行排序。

函数原型:

 usort(array &$array, callable $callback): true

根据用户提供的比较函数,对 array 原地排序。 $callback 是一个回调函数,返回 0 数组中比较的两个元素保持现状, 返回 1 第一个参数应该被排在第二个参数之后,返回 -1 第一个参数应该被排在第二个参数之前。

如下示例中,从小到大排序。

示例1,一维数组:

<?php
function cmp($a, $b) {
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

foreach ($a as $key => $value) {
    echo "$key: $value\n";
}

// 输出
// 0: 1
// 1: 2
// 2: 3
// 3: 5
// 4: 6

示例2:

<?php
function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$arr = array(3, 2, 5, 6, 1);

usort($arr, function ($a, $b) {
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
});

示例3,多维数组:

<?php
function cmp($a, $b)
{
    return strcmp($a["fruit"], $b["fruit"]);
}

$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";

usort($fruits, "cmp");

foreach ($fruits as $key => $value) {
    echo "\$fruits[$key]: " . $value["fruit"] . "\n";
}

示例4, 使用一个对象的成员函数排序:

<?php
class TestObj 
{
    private string $name;

    function __construct($name)
    {
        $this->name = $name;
    }

    /* This is the static comparing function: */
    static function cmp_obj($a, $b)
    {
        return strtolower($a->name) <=> strtolower($b->name);
    }
}

$a[] = new TestObj("c");
$a[] = new TestObj("b");
$a[] = new TestObj("d");

usort($a, [TestObj::class, "cmp_obj"]);

foreach ($a as $item) {
    echo $item->name . "\n";
}

<=>是一个太空船运算符(Spaceship Operator),也称为组合比较运算符,是一个三向比较运算符,它可以执行两个操作数之间的大于,小于和相等的比较。 例如:$c = $a <=> $b;,这相当于 $c = ($a < $b) ? -1 : (($a > $b) ? 1 : 0);

示例5,使用闭包对多维数组进行排序:

<?php
$array[0] = array('key_a' => 'z', 'key_b' => 'c');
$array[1] = array('key_a' => 'x', 'key_b' => 'b');
$array[2] = array('key_a' => 'y', 'key_b' => 'a');

function build_sorter($key) {
    return function ($a, $b) use ($key) {
        return strnatcmp($a[$key], $b[$key]);
    };
}

usort($array, build_sorter('key_b'));

foreach ($array as $item) {
    echo $item['key_a'] . ', ' . $item['key_b'] . "\n";
}

示例5, 使用太空船运算符排序:

<?php
$people[0] = ['first' => 'Adam', 'last' => 'West'];
$people[1] = ['first' => 'Alec', 'last' => 'Baldwin'];
$people[2] = ['first' => 'Adam', 'last' => 'Baldwin'];

function sorter(array $a, array $b) {
    return [$a['last'], $a['first']] <=> [$b['last'], $b['first']];
}

usort($people, 'sorter');

foreach ($people as $person) {
    print $person['last'] . ', ' . $person['first'] . PHP_EOL;
}

[$a['last'], $a['first']] <=> [$b['last'], $b['first']]; 的逻辑:

1. 首先比较 $a['last'] 和 $b['last']。
* 如果 $a['last'] 小于 $b['last'],返回 -1。
* 如果 $a['last'] 等于 $b['last'],继续比较 $a['first'] 和 $b['first']。
* 如果 $a['last'] 大于 $b['last'],返回 1。

2. 如果 $a['last'] 和 $b['last'] 相等,则比较 $a['first'] 和 $b['first']。
* 如果 $a['first'] 小于 $b['first'],返回 -1。
* 如果 $a['first'] 等于 $b['first'],返回 0。
* 如果 $a['first'] 大于 $b['first'],返回 1。

这个比较逻辑在处理像名字这样的数据时特别有用,因为它首先比较姓氏(last),如果姓氏相同,则进一步比较名字(first)。 这种比较方式通常用于按照人类姓名的自然排序方式进行排序。

其他常见排序函数,如:

  • uasort() - 使用用户自定义的比较函数对数组进行排序并保持索引关联
  • uksort() - 使用用户自定义的比较函数对数组中的键名进行排序

具体参考 https://www.php.net/manual/zh/array.sorting.php

debug_backtrace

开发中有没有碰到过这样一种情况,当一个方法被调用时,想追溯具体的调用栈。

这里就可以使用 debug_backtrace() 函数。debug_backtrace() 函数在 PHP 中返回一个包含调用堆栈信息的数组。 每个数组元素代表堆栈中的一个调用层级,是一个关联数组,包含以下可能的键(键的存在取决于调用堆栈的上下文):

file:包含当前脚本的完整路径和文件名。
line:包含当前脚本中的行号。
function:包含被调用的函数名,如果是类的方法则格式为 ClassName::methodName。
class:包含被调用函数的类名(如果存在的话)。
type:如果是对象的方法调用,这里可能是 "->"(对象实例)或 "::"(静态方法)。
args:包含传递给函数的参数列表。每个参数都是一个元素,如果是对象或数组,则是一个对象或数组的表示。
object:如果是对象的方法调用,这里会包含对象的实例。

请注意,debug_backtrace() 返回的数组中的元素是按照调用堆栈的顺序排列的, 索引 0 通常表示 debug_backtrace() 函数本身的调用, 索引 1 表示调用 debug_backtrace() 的函数或方法,依此类推。 在实际使用中,可能会有所不同,与PHP版本也有一定的关系,具体可以调试看一下。

以下是一个简化的示例,展示了 debug_backtrace() 可能返回的数据结构:

array(  
    0 => array(  
        'file' => '/path/to/script.php',  
        'line' => 12,  
        'function' => 'debug_backtrace',  
        'args' => array(),  
    ),  
    1 => array(  
        'file' => '/path/to/script.php',  
        'line' => 20, // 这是调用 debug_backtrace 的代码行  
        'function' => 'B',  
        'class' => 'MyClass',  
        'type' => '->',  
        'args' => array(), // B 方法的参数列表  
        'object' => object(MyClass) // 调用 B 方法的对象实例  
    ),  
    2 => array(  
        'file' => '/path/to/script.php',  
        'line' => 30, // 这是调用 B 方法的代码行(例如 A 方法)  
        'function' => 'A',  
        'class' => 'MyClass',  
        'type' => '->',  
        'args' => array(), // A 方法的参数列表  
        'object' => object(MyClass) // 调用 A 方法的对象实例  
    ),  
    // ... 可能还有更多层级的调用信息  
);
  • 看一个类中方法调用的例子
<?php
class MyClass {  
    public function A() {  
        $this->B();  
    }  
  
    public function B() {  
        $trace = debug_backtrace();  

        var_dump($trace);
    }  
}  
  
$obj = new MyClass();  
$obj->A();

输出:

array(2) {
  [0]=>
  array(7) {
    ["file"]=>
    string(15) "/box/script.php"
    ["line"]=>
    int(4)
    ["function"]=>
    string(1) "B"
    ["class"]=>
    string(7) "MyClass"
    ["object"]=>
    object(MyClass)#1 (0) {
    }
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(7) {
    ["file"]=>
    string(15) "/box/script.php"
    ["line"]=>
    int(18)
    ["function"]=>
    string(1) "A"
    ["class"]=>
    string(7) "MyClass"
    ["object"]=>
    object(MyClass)#1 (0) {
    }
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
}

可以看到 $trace[1] 表示调用 B 的上一层方法,即 A 。 如果存在类的继承关系,子类调用父类方法,则该方法还是在子类中,注意调试区别。

  • 再看一个函数调用的例子
<?php
// filename: /tmp/a.php
function a_test($str)
{
    var_dump(debug_backtrace());
}

a_test('friend');
?>

<?php
// filename: /tmp/b.php
include_once '/tmp/a.php';

执行 /tmp/b.php,输出:

array(2) {
[0]=>
array(4) {
    ["file"] => string(10) "/tmp/a.php"
    ["line"] => int(10)
    ["function"] => string(6) "a_test"
    ["args"]=>
    array(1) {
      [0] => &string(6) "friend"
    }
}
[1]=>
array(4) {
    ["file"] => string(10) "/tmp/b.php"
    ["line"] => int(2)
    ["args"] =>
    array(1) {
      [0] => string(10) "/tmp/a.php"
    }
    ["function"] => string(12) "include_once"
  }
}

具体参考 https://www.php.net/manual/zh/function.debug-backtrace.php






参考资料


返回