PHP编程遇到过的细节问题(总结)

记录

1. 网络地址监听

习惯本地开发调试了,忘记了127.0.0.1只能是本地,监听所有IP要监听 0.0.0.0。

2. use语句

use语句只在它所声明的物理文件内有效,use定义别名之后,无法使用之前的名字。

  • use,给命名空间定义一个别名,用于简化复杂的命名空间调用。use abc as d,调用d下面的类、函数、常量等成员时  dson();
  • use,从命名空间引入类,引入后可直接使用,不需要前缀(引入时可以定义别名)。use abc as d,使用该类时  new d();
  • use,从命名空间引入函数,引入后可直接使用,不需要前缀(引入时可以定义别名)。use function abc as d,使用该函数时  d();
  • use,从命名空间引入常量,引入后可直接使用,不需要前缀(引入时可以定义别名)。use const abc as d,使用该常量时  d;

3. 向数据库Mysql写入html

向数据库Mysql写入html语句时被自动转义,原来是自己转义写入,取出后转义回去,发现转义回不去了。调试了一下发现写入时转了两次,现在还没找到原因。

4. include和require

使用include和require时,相对路径是以运行的脚本所在的路径作为标准,文件之间嵌套的include都以运行脚本所在的目录作为标准。a 包括 b,b 包括 c ,b是相对a所在的目录包括c。在PHP中不能以 “/”代表绝对路径(应该是要进行相关设置之后才可以,例如php.ini里的include_path),正常情况下可以使用超全局变量 $_SERVER["DOCUMENT_ROOT"]代表根目录路径。

5. 多文件上传

多文件同时上传(name[],指定name为数组的形式)时全局变量$_FILE 的层级为 $_FILE[param][tmp_name/name/size][0/1/2/3]。不同的name则直接$_FILE[name]完事。

6. PHP类成员

PHP类成员必须加修饰符 public/private/protected。__DIR__,这会获取当前脚本所在目录的路径

7. <style>标签

<style>标签支持load事件。可以div内放一个标签实现div的加载事件。

8. 正则匹配

正则匹配时当“.”在“[ ]”内时代表标点符号,不需要转义,其它元字符则需要转义。非贪婪匹配 (.*?)

9. intdiv()

PHP7引入了intdiv()的新函数,它执行操作数的整数除法并返回结果为 int 类型。inidiv(10,3)等于3;

10. Tp6

开发模式下经常用的配置可以放在'.env'目录下,有了.env 文件,不用每次去改config里的配置,.env用在开发过程中模拟环境变量配置(官方建议该文件在服务器部署的时候忽略),删除.env,如果 .env 还有其他配置项,就意味着你还会丢失配置项,一般不建议这样做,只是在 .env 中关闭调试即可。实测.env在调试模式开启、关闭下都可以获取值。

11. 循环使用多条件

PHP的for循环使用多条件时,跟IF中的语法是一致的,如for($k=$pagenum-2;$k>=-1 && $k<$pagenum && $k>($pagenum-3);$k++),多变量的多条件则用逗号分隔分别进行定义。

12. 下级目录操作上级目录文件

下级目录的PHP文件操作上级目录文件时会受到php.ini、目录内的.user.ini 的open_basedir配置项所指定的路径限制;

13. Nginx不支持中文目录

Nginx不支持中文目录解决办法,首先将xftp之类的软件的传输编码改为UTF-8,在Nginx配置文件内加上Chatset utf-8; 指令,重启Nginx即可;

14. 生产版本和开发版本

PHP生产环境版本指只启用了部分拓展,而开发环境版本则启用了所有可以加载的拓展。

15. 一些可能比较少见的PHP语法:

  • 类的构造方法没有参数时,实例化类可以省略括号,例如 new A;
  • (new app())->console->run(); 这是在tp6见到的语法。
  • 箭头函数的基本语法为 fn (argument_list) => expr。(PHP7.4的特性)
    <?php
    $y = 1;
    $fn1 = fn($x) => $x + $y;
    // 相当于 using $y by value:
    $fn2 = function ($x) use ($y) {
        return $x + $y;
    };
    var_export($fn1(3));
  • 流程控制(包括if、for、foreach、while、swich)等语句的替代语句,左花括号换成冒号(:),右花括号换成(endif、endfor等)。
    if  (a > b):
        echo "this";
    end if
    
  • $fp=fopen($file_path,"r") or die("文件不存在!");

16. 错误日志

error_log =/usr/local/error.log,配置项内开启PHP错误日志记录,Thinkphp会自动记录错误日志。

17. php正则

php正则可以自定义开头和结尾标识符的。/w/、#w#、@w@是等价的,在正则中匹配反斜杠需要四个,才能匹配(也可以,但是官方说要四个);讲道理,php单引号内不换进行转义使用应该也可以,但事实并非如此。在java内元字符前面 s ,都是两个杠,字符串转义 之后是一个斜杠,正则本身也需要转义。所以java匹配需要四个,经过字符串正则两道转义。

18. PHP 通过多进程实现异步操作。

<?php
$pid = pcntl_fork();
if ($pid == 0) {
    //子进程
    //模拟发送邮件
    sleep(30);//发送邮件花费30秒
    exit(0);
}

pcntl_waitpid($pid, $status, WNOHANG);
echo "发送邮件中";

相关文章:PHP实用函数记录PHP实用函数记录2

19. UTF-8 BOM

window记事本编辑过的PHP文件会变成utf8 bom格式 ,每一个bom格式的php文件被加载的时候都会导致输出一个特殊字符。

20. 函数内声明全局变量

global $variant;
$variant = 1;

21. namespace

PHP 命名空间可以解决以下两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
PHP普通变量和define声明的常量不受namespace的约束,也就是说include文件时,普通变量重复声明时,前一个会被后一个覆盖

22. clone关键字

在 PHP 中可以使用 clone 关键字克隆对象,语法格式如下:

克隆对象名称 = clone 原对象名称;

因为 clone 的方式实际上是对整个对象的内存区域进行了一次复制并用新的对象变量指向新的内存,因此赋值后的对象和原对象之间是相互独立的。对象克隆成功后,它们中的成员方法、属性以及值是完全相同的。如果要对克隆后副本的成员属性重新赋值,可以使用PHP魔术方法中的 __clone() 方法。

23. new static

new self 和 new static 的区别:两者都是实例化自身,区别在于继承。如果没有继承,则两者返回的实例都是属于一个类;如果有继承,子类调用该方法,new self 仍然返回原类的实例,而 new static 返回实际子类的实例。这就是延迟静态绑定,static 的值,使用的是最后实际调用那个方法的类。

yield生成器

1.什么是yield

yield可以理解为中断函数并返回指定值。

函数内可以yield多少次,外部对这个生成器函数就可以遍历循环多少次。

PHP数组传递不是引用传递,而是值传递;在调用函数时通过将PHP数组作为实参赋给形参,在函数中修改,并不会影响到数组本身,说明此过程中的传递是值传递,数组变量并非指向此数组本身的引用。

遍历一个从函数内返回的数组,按照值传递的机制,会创建一个对应大小的数组。按照yield则每次返回一个元素

  • yield是生成器所需要的关键字,必须在函数内部,有yield的函数叫做"生成器函数"
  • 调用生成器函数时,函数将返回一个继承了Iterator的生成器
  • yield作为表达式使用时,可将一个值加入到生成器中进行遍历,遍历完会中断下面的语句运行,并且保存状态,当下次遍历时会继续执行(这就是while(true)没有造成阻塞的原因)
  • 当send传入参数时,yield可作为一个变量使用,这个变量等于传入的参数

2.大文件读取

通过生成器+按行读取,可以使用少量的内存,读取大文件,按行读取,每次只读取指定行的数据。

数组、生成器实现了Iterator接口。

拓展安装

拓展库:https://pecl.php.net/

1. 安装ssh2

pecl install ssh2-1.1

实用函数

1.中文截取

<?php

/*
 * 中文截取
 * */
function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
{
    if (extension_loaded('mbstring') === true) {
        $string_length = (is_null($encoding) === true) ? mb_strlen($string) : mb_strlen($string, $encoding);

        if ($start < 0) {
            $start = max(0, $string_length + $start);
        } else if ($start > $string_length) {
            $start = $string_length;
        }

        if ($length < 0) {
            $length = max(0, $string_length - $start + $length);
        } else if ((is_null($length) === true) || ($length > $string_length)) {
            $length = $string_length;
        }

        if (($start + $length) > $string_length) {
            $length = $string_length - $start;
        }

        if (is_null($encoding) === true) {
            return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length);
        }

        return mb_substr($string, 0, $start, $encoding) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length, $encoding);
    }

    return (is_null($length) === true) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length);
}

2. 全局错误捕获

<?php

/*
 * 全局错误处理
 * */
set_error_handler(function($errno, $errstr, $errfile, $errline)
{
    file_put_contents('log.txt',$errno.",".$errstr.",".$errfile.",".$errline."n",FILE_APPEND);
    exit();
});


set_exception_handler(function($exception)
{
    file_put_contents('log.txt',$exception->getMessage().','.$exception->getTraceAsString()."n",FILE_APPEND);
    exit();
});

PHP代码混淆 

<?php

/**
 * @param $length
 * @return string
 * 随机字符串
 */
function RandAbc($length = "")
{ // 返回随机字符串
    $str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    return str_shuffle($str);
}

/**
 * @param $code
 * @param $key
 * @return string
 * @throws Exception
 * 代码加密
 */
function encrypt($code, $key)
{
    /* 生成随机的初始化向量(IV) */
    $iv = random_bytes(16);
    /* 对代码进行加密 */
    $encryptedCode = openssl_encrypt($code, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    /* 将加密后的代码和IV进行Base64编码 */
    $encodedCode = base64_encode($encryptedCode);
    $encodedIV = base64_encode($iv);
    /* 创建解密函数的代码 */
    $function = "function decrypt(\$encryptedCode, \$key, \$iv) {\n";
    $function .= "  \$decodedCode = base64_decode(\$encryptedCode);\n";
    $function .= "  \$decodedIV = base64_decode(\$iv);\n";
    $function .= "  return openssl_decrypt(\$decodedCode, 'aes-256-cbc', \$key, OPENSSL_RAW_DATA, \$decodedIV);\n";
    $function .= "}\n";
    /* 返回加密后的代码和解密函数的代码 */
    return "<?php \n{$function}eval('?>'.decrypt('{$encodedCode}', '{$key}', '{$encodedIV}'));";
}

/**
 * 去除所有注释
 */
function remove($code)
{
    $pattern = '/(\/\*[\s\S]*?\*\/)|(\/\/[^\'\"\/]*?\n)/s';
    return preg_replace($pattern, '', $code);
}

$filename = 'index.php'; //要加密的文件
$T_k1 = RandAbc(); //随机密匙1
$T_k2 = RandAbc(); //随机密匙2


$vstr = encrypt(remove(file_get_contents($filename)), 'Nicen.cn)');

$v1 = base64_encode($vstr);
$c = strtr($v1, $T_k1, $T_k2); //根据密匙替换对应字符。
$c = $T_k1 . $T_k2 . $c;
$q1 = "O00O0O";
$q2 = "O0O000";
$q3 = "O0OO00";
$q4 = "OO0O00";
$q5 = "OO0000";
$q6 = "O00OO0";
$s = '$' . $q6 . '=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$' . $q1 . '=$' . $q6 . '[3].$' . $q6 . '[6].$' . $q6 . '[33].$' . $q6 . '[30];$' . $q3 . '=$' . $q6 . '[33].$' . $q6 . '[10].$' . $q6 . '[24].$' . $q6 . '[10].$' . $q6 . '[24];$' . $q4 . '=$' . $q3 . '[0].$' . $q6 . '[18].$' . $q6 . '[3].$' . $q3 . '[0].$' . $q3 . '[1].$' . $q6 . '[24];$' . $q5 . '=$' . $q6 . '[7].$' . $q6 . '[13];$' . $q1 . '.=$' . $q6 . '[22].$' . $q6 . '[36].$' . $q6 . '[29].$' . $q6 . '[26].$' . $q6 . '[30].$' . $q6 . '[32].$' . $q6 . '[35].$' . $q6 . '[26].$' . $q6 . '[30];eval($' . $q1 . '("' . base64_encode('$' . $q2 . '="' . $c . '";eval(\'?>\'.$' . $q1 . '($' . $q3 . '($' . $q4 . '($' . $q2 . ',$' . $q5 . '*2),$' . $q4 . '($' . $q2 . ',$' . $q5 . ',$' . $q5 . '),$' . $q4 . '($' . $q2 . ',0,$' . $q5 . '))));') . '"));';

$s = '<?php ' . "\n" . $s . "\n" . ' ?>';
file_put_contents('a.php', $s);

 

PHP新特性

1. Spread 运算符(7.4)

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];

... 符号在不同场景下的使用:将数组展开为函数参数列表、将可迭代对象展开为函数参数列表

2. 对称性数组解构(7.1)

[$id1, $name1] = $data[0];

3. 键名解构(7.1)

list("id" => $id1, "name" => $name1) = $data[0];
["id" => $id1, "name" => $name1] = $data[0];

4. 解构支持引用赋值(7.2)

[&$a, [$b, &$c]] = $d
补充
属性值类型声明、匿名函数、spread数组内展开(7.4) 

PHP-CLI

1.相关命令

  • php --ini ,查看PHP的配置信息
  • php --info,信息概览
  • php-config --prefix,查看PHP安装路径

相关文档:https://www.php.net/manual/zh/features.commandline.php

PHP反射

PHP Reflection是一个内置的PHP类,它提供了一种反射PHP类、方法、属性的方法,可以让开发者在运行时动态地获取类、方法、属性的信息。

1.ReflectionClass

ReflectionClass,用于获取类的信息。$reflection = new ReflectionClass('类名');

可以使用这个类获取类的信息,例如类的名称、父类、接口、属性、方法等。

2.ReflectionMethod

ReflectionMethod,用于获取方法的信息。$reflection = new ReflectionMethod('类名', '方法名');

可以使用这个类获取方法的信息,例如方法的名称、参数、返回值等。

3.ReflectionProperty

ReflectionProperty,用于获取属性的信息。$reflection = new ReflectionProperty('类名', '属性名');

可以使用这个类获取属性的信息,例如属性的名称、可见性等。

使用这些类,可以获取类、方法、属性的各种信息,例如名称、可见性、注释、参数、返回值等。这些信息可以用于调试、自动化测试、代码生成等场景。