REC
首页
文章分类
源码资源
技术教程
程序软件
文创娱乐
玄学修炼
关于我们
其他页面
网站统计
友情链接
用户留言
高清壁纸
关于易航
热门文章
Joe再续前缘主题 - 搭建本站同款网站
易航网址导航系统 – 功能强大,轻量易用
JsonDb-PHP轻量级文件数据库系统
Typecho一键调整网站为冬天情景插件
V免签全开源免签约码支付系统(支持:支付宝 微信 QQ)
标签搜索
PHP
Web前端
网站源码
PHP源码
Typecho
Typecho插件
课程资料
Windows程序
Android软件
武术内功
HTML源码
Web
Joe主题
Python
Windows
国漫
网络协议
MySQL
NodeJs
小说
发布
登录
注册
找到
51
篇与
PHP
相关的结果
- 第 4 页
2024-09-23
Web 开发 2025:PHP 依然有一席之地
PHP图片 在 2025 年即将到来之际,许多新晋 Web 开发人员都在思考一个问题:学习 PHP 还有必要吗? PHP 作为 Web 开发领域的元老,几十年来一直占据着主导地位,其影响力不容小觑。超过 75% 的网站都运行在 WordPress、Drupal 和 Joomla 等平台之上,而这些平台正是由 PHP 驱动的。然而,随着新兴编程语言和框架的涌现,PHP 的未来似乎蒙上了一层阴影,学习它的价值也因此受到质疑。 为了帮助您做出明智的决定,我们将深入探讨在 2025 年学习 PHP 的利弊,并分析其未来发展趋势。 1、PHP 的持续相关性 尽管 Python、JavaScript 和 Ruby 等新兴语言来势汹汹,但 PHP 在 Web 开发领域的地位依然不可撼动。尤其是 WordPress 等巨头平台,凭借 PHP 构建,牢牢占据着内容管理系统(CMS)市场的半壁江山。因此,对于专注于网站、博客或电子商务解决方案开发的开发者来说,掌握 PHP 依然是至关重要的技能。 2、框架和工具 现代 PHP 开发早已摆脱了过去的繁琐,这主要归功于 Laravel 等框架的兴起。Laravel 以其优雅的语法和强大的功能著称,为开发者提供了前所未有的便捷体验。Artisan 命令行工具、Eloquent ORM 和 Blade 模板引擎等更是如虎添翼,极大提升了开发效率。类似的,其他流行框架如 Django 和 Ruby on Rails 也采用了类似的设计理念, 致力于为开发者提供高效愉悦的开发体验。 3、PHP 8.x 和性能提升 PHP 并未故步自封,而是不断发展进化。PHP 8.0 及其后续版本的发布,带来了显著的性能提升和安全增强。其中,全新的 JIT 编译器极大提升了代码执行速度, 使 PHP 在与其他语言的竞争中更具优势。此外,PHP 还积极拥抱其他语言,支持与 Node.js 和 Python 等语言的集成,进一步拓展了其应用领域。 4、繁荣的社区和就业机会 PHP 并未故步自封,而是不断发展进化。PHP 8.0 及其后续版本的发布,带来了显著的性能提升和安全增强。其中,全新的 JIT 编译器极大提升了代码执行速度, 使 PHP 在与其他语言的竞争中更具优势。此外,PHP 还积极拥抱其他语言,支持与 Node.js 和 Python 等语言的集成,进一步拓展了其应用领域。 5、PHP 与新语言 虽然 PHP 宝刀未老,但我们也必须承认,一些新兴语言和框架在某些方面已经走在了前面,尤其是在现代 Web 应用开发领域。 JavaScript (Node.js): Node.js 已然成为全栈开发的热门选择,其最大的优势在于允许开发者使用 JavaScript 同时进行前端和后端开发。随着 React、Vue 等 JavaScript 框架的流行,越来越多的开发者投入 Node.js 的怀抱,享受其带来的流畅开发体验。 Python (Django/Flask): Python 以其简洁易读的语法著称,并且在数据科学和人工智能领域得到了广泛应用。这使得 Python 也成为了 Web 开发领域的热门选择。与 PHP 相比,Django 和 Flask 等 Python 框架提供了更强大的功能和更简洁的语法,对新一代开发者更具吸引力。 Go: Go 语言以其超快的速度和简洁的语法而闻名,在高性能应用和微服务领域越来越受欢迎。 尽管 PHP 依然占据一席之地,但学习 JavaScript 或 Python 等语言可以为你打开更多大门,让你在数据科学、机器学习或移动应用开发等领域大展拳脚。 6、您应该在 2025 年学习 PHP 吗? 如果你专注于开发基于 CMS 的项目,或者需要维护现有的 PHP 系统,那么学习 PHP 依然是明智之举。 而如果你立志成为全栈开发者,JavaScript 或 Python 或许是更佳选择。 当然,鱼和熊掌也可兼得。精通 PHP 的同时掌握前端开发技能,无疑会让你在求职市场上脱颖而出。更重要的是,借助 Laravel 等现代框架,PHP 开发体验也丝毫不逊色于其他语言。 PHP 将成为 2025 年最受好评的编程语言 PHP 将成为 2025 年最受好评的编程语言图片 这张图表预测了到 2025 年,PHP 与其他主流编程语言(JavaScript、Python、Java 和 Go)在受欢迎程度、就业机会和 Web 开发领域的主导地位。 1、受欢迎程度(%):该指标反映了开发者对每种语言的使用率和满意度。 JavaScript 以 95% 的高比例遥遥领先,Python 紧随其后,占比 92%。 PHP 依然保持着 80% 的强劲势头,表明其与 Web 开发的密切联系。 Java 和 Go 分别以 75% 和 60% 的比例位居其后。 2、就业机会(%):该指标预测了市场对掌握每种语言的开发者的需求程度。 PHP 的就业市场份额高达 82%,凸显了其强劲的就业前景,尤其是在 Web 开发和 CMS 平台领域。 JavaScript 和 Python 分别以 90% 和 88% 的比例领先,而 Java 和 Go 也保持稳定,分别占比 80% 和 65%。 3、Web 开发主导地位(%):该指标衡量每种语言在 Web 开发领域的影响力。 JavaScript 在前端开发领域占据绝对优势,占比高达 95%。 PHP 作为服务器端开发的传统优势依然存在,占比 85%,这意味着它将继续为大量 Web 应用提供支持,尤其是 WordPress 和 Laravel 等平台。 Python (70%) 和 Java (50%) 在 Web 应用开发中也占有一席之地,而 Go (40%) 正在某些细分领域逐渐崭露头角。 总而言之,这份图表强调了 PHP 在 Web 开发领域的重要地位,它依然是需求旺盛的主流语言之一,对于从事 CMS 平台和遗留系统开发的开发者来说更是不可或缺的技能。 结论 展望 2025 年,PHP 在 Web 开发领域依然占据着不可忽视的地位,尤其是在 CMS 开发和遗留系统维护方面。 当然,为了拓宽职业发展道路,或者进军人工智能、数据科学等前沿领域,学习 JavaScript 或 Python 等语言也是非常有益的。 总而言之,PHP 本身在不断发展进步,加之 Laravel 等现代框架的助力,对于有志于成为 Web 开发者的新人来说,PHP 依然是一项值得学习的宝贵技能,尤其是在 Web 开发领域。
技术教程
# PHP
# Web
易航
1年前
0
92
0
2024-09-11
PHP异步协程开发:优化邮件发送的速度与稳定性
在现代的互联网应用中,邮件发送是一个非常重要的功能,无论是用户注册验证、订单确认还是密码重置等等,都离不开邮件的发送。然而,传统的同步邮件发送方式在处理大量邮件发送时往往效率低下且不稳定。为了解决这个问题,我们可以使用PHP的异步协程开发,通过并发发送邮件,提高发送速度和稳定性。 本文将详细介绍使用PHP异步协程来优化邮件发送的方法,并通过具体的代码示例来说明。 一、PHP异步协程简介 PHP异步协程是指通过利用事件循环机制,将多个任务并发执行,以提高应用程序的执行效率。在传统的PHP开发中,我们通过多线程或多进程来实现并发处理,但这种方式会增加系统资源的开销。而PHP异步协程则通过单线程来同时处理多个任务,不会引起资源开销过大的问题。 二、优化邮件发送的原理 传统的邮件发送方式是同步的,即每发送一封邮件,都要等待邮件发送完成后再发送下一封。这样一来,当需要发送大量邮件时,会花费很长的时间,而且容易造成服务器的负载过大。 而通过PHP异步协程开发,我们可以将邮件发送任务封装成一个异步协程,然后一次性并发发送多个任务,提高发送效率。同时,由于使用了异步协程,可以避免等待发送完成的时间,从而提高整体的稳定性。 三、使用PHP异步协程发送邮件的代码示例 下面是一个使用PHP异步协程发送邮件的代码示例: use SwooleCoroutine; use SwooleCoroutineChannel; use PHPMailerPHPMailerPHPMailer; function sendMail($to, $subject, $body) { go(function () use ($to, $subject, $body) { $mail = new PHPMailer; $mail->isSMTP(); $mail->Host = 'smtp.example.com'; $mail->SMTPAuth = true; $mail->Username = 'username'; $mail->Password = 'password'; $mail->SMTPSecure = 'tls'; $mail->Port = 587; $mail->setFrom('from@example.com'); $mail->addAddress($to); $mail->Subject = $subject; $mail->Body = $body; if ($mail->send()) { echo "发送成功 "; } else { echo "发送失败:" . $mail->ErrorInfo . " "; } }); } $channel = new Channel(); go(function () use ($channel) { for ($i = 1; $i <= 100; $i++) { $channel->push(["to@example.com", "测试邮件{$i}", "这是一封测试邮件"]); } $channel->close(); }); go(function () use ($channel) { while ($data = $channel->pop()) { sendMail($data[0], $data[1], $data[2]); } }); Coroutine::create(function () { Coroutine::sleep(1); // 等待所有邮件发送完成 swoole_event_exit(); // 退出事件循环 });上述代码首先定义了一个 sendMail 函数,用于发送邮件。在 sendMail 函数内部,我们使用了 PHPMailer 库来实现邮件的发送。在异步协程中发送邮件时,需要等待邮件发送完成的时间,因此我们使用了协程的方式来进行处理,保证发送的效率。 然后,我们创建了一个 Channel 通道,将待发送的邮件信息推入该通道,并在另外一个协程中进行发送。 最后,我们使用协程的方式来等待所有邮件发送完成,并退出事件循环。 四、总结 通过使用PHP异步协程开发,我们可以优化邮件发送的速度和稳定性,提高应用程序的性能和响应能力。同时,异步协程的使用也能减少服务器资源的开销,更好地满足用户的需求。 当我们在开发邮件发送功能时,可以参考上述的代码示例,并根据实际需求进行相应的调整和优化。希望本文对广大开发者在优化邮件发送方面有所帮助。
技术教程
# PHP
易航
1年前
0
41
0
2024-08-24
PHPY 打破语言界限,使PHP引入Python生态,开创PHP语言AI编程时代!
PHPY是什么? phpy 是识沃团队最新推出的开源项目,目标是为 PHP 引入 Python 生态,来弥补 PHP 生态的空缺和不足。phpy 使得 PHP 可以调用所有 Python 的包。包括当下非常流行的 AI 库,如:PyTorch、transformers、TensorFlow 等包括当下非常流行的 AI 库,如 PyTorch、transformers、TensorFlow 等,以及科学计算库,如 Numpy、Pandas、Scikit 等,还可以使用图形界面库,如 PyQt、wxPython 等。 不建议在 php-fpm/apache 短生命周期运行环境下使用,频繁地导入/销毁模块的开销会消耗大量资源环境 Linux环境:Ubuntu 22.04.3 LTS PHP版本:PHP 8.1 或以上版本 Python 3.10 或以上版本 安装PHP8.3 下载 wget wget https://www.php.net/distributions/php-8.3.0.tar.gz tar -zxvf php-8.3.0.tar.gz下载地址:https://www.php.net/downloads安装依赖包 sudo apt-get install libfcgi-dev libfcgi0ldbl libjpeg-turbo8-dev libmcrypt-dev libssl-dev libc-client2007e libc-client2007e-dev libxml2-dev libbz2-dev libcurl4-openssl-dev libjpeg-dev libpng-dev libfreetype6-dev libkrb5-dev libpq-dev libxml2-dev libxslt1-dev libzip-dev libsqlite3-dev libonig-dev pkg-config libxml2-dev libkrb5-dev libssl-dev libsqlite3 libbz2-dev libpng-dev libjpg-dev libfreetype6-dev libc-client2007e-dev libonig-dev libreadline-dev libxslt-dev libzip-dev如果安装的依赖包不存在,请通过命令:apt-cache search freetype 查找相应的安装包安装即可编译 ./configure \ --prefix=/usr/local/php-8.3 \ --with-config-file-path=/usr/local/php-8.3/etc \ --with-zlib-dir \ --with-freetype \ --enable-mbstring \ --enable-soap \ --enable-calendar \ --with-curl \ --with-zlib \ --enable-gd \ --disable-rpath \ --enable-inline-optimization \ --with-bz2 \ --with-zlib \ --enable-sockets \ --enable-sysvsem \ --enable-sysvshm \ --enable-pcntl \ --enable-mbregex \ --enable-exif \ --enable-bcmath \ --with-mhash \ --with-zip \ --with-pdo-mysql \ --with-mysqli \ --with-mysql-sock=/var/run/mysqld/mysqld.sock \ --with-jpeg \ --with-openssl \ --with-fpm-user=www \ --with-fpm-group=www \ --with-libdir=/lib/x86_64-linux-gnu \ --enable-ftp \ --with-kerberos \ --with-gettext \ --with-xmlrpc \ --with-xsl \ --enable-opcache \ --enable-intl \ --with-pear \ --enable-fpm编译安装 make make install配置文件 核心 php.ini 配置文件cp php.ini-production /usr/local/php-8.3/etc/php.ini查看PHP版本/usr/local/php-8.3/bin/php -v PHP 8.3.0 (cli) (built: Dec 5 2023 20:03:56) (NTS) Copyright (c) The PHP Group Zend Engine v4.3.0, Copyright (c) Zend Technologies安装Python 安装Anaconda Anaconda 是一个开源的Anaconda是专注于数据分析的Python发行版本,包含了conda、Python等190多个科学包及其依赖项。 Anaconda就是可以便捷获取包且对包能够进行管理,包括了python和很多常见的软件库和一个包管理器conda。常见的科学计算类的库都包含在里面了,使得安装比常规python安装要容易,同时对环境可以统一管理的发行版本。 下载地址:https://www.anaconda.com/download#downloads wget https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh命令行中切换到anaconda文件所在目录 sh Anaconda3-2022.05-Linux-x86_64.sh accept the license terms--yes默认安装路径,/home/用户名/anaconda3=查看Anaconda版本 终端输入 conda --version 或者 conda -V /home/www/anaconda3/condabin/conda --version conda 23.7.4创建虚拟环境 $ conda create -n tinywan-python310 python=3.10 Collecting package metadata (current_repodata.json): done Solving environment: done ==> WARNING: A newer version of conda exists. <== current version: 23.7.4 latest version: 23.11.0 Please update conda by running $ conda update -n base -c defaults conda Or to minimize the number of packages updated during conda update use conda install conda=23.11.0 ## Package Plan ## environment location: /home/www/anaconda3/envs/tinywan-python310 added / updated specs: - python=3.10 The following packages will be downloaded: package | build ---------------------------|----------------- pip-23.3.1 | py310h06a4308_0 2.7 MB python-3.10.13 | h955ad1f_0 26.8 MB setuptools-68.0.0 | py310h06a4308_0 936 KB wheel-0.41.2 | py310h06a4308_0 109 KB ------------------------------------------------------------ Total: 30.5 MB The following NEW packages will be INSTALLED: _libgcc_mutex pkgs/main/linux-64::_libgcc_mutex-0.1-main _openmp_mutex pkgs/main/linux-64::_openmp_mutex-5.1-1_gnu bzip2 pkgs/main/linux-64::bzip2-1.0.8-h7b6447c_0 ca-certificates pkgs/main/linux-64::ca-certificates-2023.08.22-h06a4308_0 ld_impl_linux-64 pkgs/main/linux-64::ld_impl_linux-64-2.38-h1181459_1 libffi pkgs/main/linux-64::libffi-3.4.4-h6a678d5_0 libgcc-ng pkgs/main/linux-64::libgcc-ng-11.2.0-h1234567_1 libgomp pkgs/main/linux-64::libgomp-11.2.0-h1234567_1 libstdcxx-ng pkgs/main/linux-64::libstdcxx-ng-11.2.0-h1234567_1 libuuid pkgs/main/linux-64::libuuid-1.41.5-h5eee18b_0 ncurses pkgs/main/linux-64::ncurses-6.4-h6a678d5_0 openssl pkgs/main/linux-64::openssl-3.0.12-h7f8727e_0 pip pkgs/main/linux-64::pip-23.3.1-py310h06a4308_0 python pkgs/main/linux-64::python-3.10.13-h955ad1f_0 readline pkgs/main/linux-64::readline-8.2-h5eee18b_0 setuptools pkgs/main/linux-64::setuptools-68.0.0-py310h06a4308_0 sqlite pkgs/main/linux-64::sqlite-3.41.2-h5eee18b_0 tk pkgs/main/linux-64::tk-8.6.12-h1ccaba5_0 tzdata pkgs/main/noarch::tzdata-2023c-h04d1e81_0 wheel pkgs/main/linux-64::wheel-0.41.2-py310h06a4308_0 xz pkgs/main/linux-64::xz-5.4.5-h5eee18b_0 zlib pkgs/main/linux-64::zlib-1.2.13-h5eee18b_0 Proceed ([y]/n)? y Downloading and Extracting Packages Preparing transaction: done Verifying transaction: done Executing transaction: done # # To activate this environment, use # # $ conda activate tinywan-python310 # # To deactivate an active environment, use # # $ conda deactivate激活虚拟环境 conda activate tinywan-test查看已有的虚拟环境 $ conda env list # conda environments: # base /home/www/anaconda3 tinywan-test * /home/www/anaconda3/envs/tinywan-test进入虚拟环境查看Python版本 (tinywan-python310) www@xxxxxxx:~/anaconda3$ python --version Python 3.10.13安装phpy 下载 git clone https://github.com/swoole/phpy.git生成 ./configure 配置文件 /usr/local/php-8.3/bin/phpize --with-php-config=/usr/local/php-8.3/bin/php-config指定配置文件 ./configure --with-php-config=/usr/local/php-8.3/bin/php-config --with-python-dir=/home/www/anaconda3/envs/tinywan-python310 --with-python-version=3.10参数说明 --with-php-config PHP配置文件 --with-python-dir Python安装目录 --with-python-version Python版本号(只能使用次版本好,如:3.10.15,则填写3.10) 编译安装 make -j4 sudo make installphp.ini 扩展添加 安装成功后,修改 php.ini ,加入 extension=phpy.so vim /usr/local/php-8.3/etc/php.ini // 添加 extension=phpy.so执行 php -m 检查是否成功加载扩展。 $ php -m |grep phpy phpy使用 案例 os.php <?php function main() { $m = PyCore::import("os"); var_dump($m instanceof PyObject); $rs = $m->uname(); echo $rs; echo $rs->version; } main()查看当前操作系统版本执行结果 /phpy/examples$ /usr/local/php-8.3/bin/php os.php bool(true) posix.uname_result(sysname='Linux', nodename='iZbp1cqx6cq0t2gpl995gqZ', release='4.15.0-137-generic', version='#141-Ubuntu SMP Fri Feb 19 13:46:27 UTC 2021', machine='x86_64')#141-Ubuntu SMP Fri Feb 19 13:46:27 UTC 2021(tinywan-python310)其他 gcc 版本升级 ubuntu18.04的Gcc7.5.0升级到9.4.01、添加Ubuntu的测试工具链 (Toolchain) PPA。这个PPA包含了最新版本的GCC,包括GCC 9: sudo add-apt-repository ppa:ubuntu-toolchain-r/test2、更新你的包列表: sudo apt-get update sudo apt-get upgrade3、查看gcc所有版本 sudo apt-cache search gcc4、安装GCC-9: sudo apt install gcc-95、为了让你的系统默认使用GCC-9,你需要更新你的update-alternatives。首先,安装GCC-9为一个可选项 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90这个命令告诉 update-alternatives 系统 GCC-9 是一个可选项,并给它一个优先级 90 。优先级最高的版本将成为默认版本。如果安装成功会是这样的: gcc -v gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~18.04)
技术教程
# PHP
# Python
易航
1年前
0
257
0
2024-08-24
PHP 8.0:新时代的编程巨浪
随着技术的不断进步,PHP也在不断演化,迎来了它的8.0版本。这一版本不仅仅是数字的增加,更是PHP语言的一次重大飞跃。今天,让我们一起探索PHP 8.0的新特性,以及它如何为开发者带来前所未有的便利。 图片 PHP 8.0简介 PHP 8.0是PHP语言的最新主要版本,它引入了许多新特性和改进,包括JIT编译器、命名参数、联合类型、属性、改进的类型系统等。这些变化旨在提高性能、增强代码的可读性和可维护性。 PHP 7与PHP 8.0的差异 PHP 8.0在多个方面与PHP 7有所不同,以下是一些主要的差异: JIT编译器:PHP 8.0引入了Just-In-Time (JIT) 编译器,这使得PHP代码的执行速度有了显著提升。 命名参数:PHP 8.0允许开发者使用命名参数,这使得函数调用更加灵活和可读。 联合类型:PHP 8.0支持联合类型,允许一个变量或函数参数接受多种类型的值。 属性:PHP 8.0引入了属性(Attributes),这是一种新的元数据语法,可以用于注解类、方法和属性。 改进的类型系统:PHP 8.0对类型系统进行了改进,包括更严格的类型检查和新的类型声明。 PHP 8.0新增功能和方法 PHP 8.0带来了许多新功能和方法,以下是一些值得关注的亮点: 图片 JIT编译器:通过在配置文件中启用JIT,可以显著提高代码执行速度。 命名参数:允许在函数调用时指定参数名称,例如: htmlspecialchars($string, double_encode: false);联合类型:允许函数参数或返回值为多种类型之一,例如: function foo(int|float $number): int|float { return $number * 2; }属性:使用属性来注解代码,例如: #[Route("/api/items/{id}", methods: ["GET"])] function getItem($id) { // 你的代码 }Match表达式:提供了一种更简洁的switch语法,例如: $status = match ($statusCode) { 200 => 'OK', 404 => 'Not Found', default => 'Unknown', };入门指导 要开始使用PHP 8.0,你需要确保你的开发环境已经支持这个版本。你可以通过以下步骤来安装或升级到PHP 8.0: 检查当前版本:首先,运行以下命令来检查你当前的PHP版本: php -v升级到PHP 8.0:如果你的系统支持包管理器,你可以通过包管理器来安装或升级到PHP 8.0。例如,在Ubuntu上,你可以使用以下命令: sudo apt update sudo apt install php8.0代码案例 让我们来看一个简单的例子,展示PHP 8.0中的一些新特性: // 命名参数 htmlspecialchars($string, double_encode: false); // 联合类型 function foo(int|float $number): int|float { return $number * 2; } // 属性 #[Route("/api/items/{id}", methods: ["GET"])] function getItem($id) { // 你的代码 } // Match表达式 $status = match ($statusCode) { 200 => 'OK', 404 => 'Not Found', default => 'Unknown', };PHP 8.0优劣势 优势: 性能提升:PHP 8.0引入了JIT编译器,显著提高了执行速度。 代码简洁:命名参数和联合类型等新特性使得代码更加简洁和易读。 开发效率:属性和其他改进减少了样板代码,提高了开发效率。 劣势: 兼容性问题:升级到PHP 8.0可能会遇到一些兼容性问题,需要对现有代码进行适配。 学习曲线:新特性可能需要开发者花费时间去学习和适应。 使用方法和场景 PHP 8.0适用于所有希望提高性能和代码质量的PHP开发者。它特别适合以下场景: 高性能应用:对于需要快速响应和高吞吐量的应用,PHP 8.0的JIT编译器是一个巨大的优势。 现代化开发:PHP 8.0的新特性使得代码更加现代化,适合追求最新技术趋势的开发者。 注意事项 在使用PHP 8.0时,开发者需要注意以下几点: 兼容性测试:在升级之前,确保对现有代码进行充分的兼容性测试。 文档学习:深入学习PHP 8.0的新特性,充分利用其带来的优势。 结尾 亲爱的开发工程师们,PHP 8.0为我们带来了新的机遇和挑战。让我们拥抱变化,勇敢地迈向新时代。记住,技术的海洋无边无际,每一次升级都是我们航行的新起点。明天,我们将继续探索PHP 8.1的奥秘,敬请期待! 在这篇文章中,我们深入探讨了PHP 8.0的新特性,并提供了入门指导、代码案例和使用场景。希望这些信息能帮助你在开发旅程中做出明智的选择,并在技术的海洋中乘风破浪。
技术教程
# PHP
易航
1年前
3
305
2
2022-12-13
ThinkPHP中日期时间区间查询以及whereTime用法
一、使用where方法进行时间的比较查询 where('create_time', '> time', '2021-8-8'); // 大于某个时间 where('create_time', '<= time', '2020-8-8'); // 小于某个时间 where('create_time', 'between time', ['2020-1-1', '2020-10-1']); // 时间区间查询二、使用whereTime方法 whereTime('birthday', '>=', '1970-10-1')->select(); // 大于某个时间 whereTime('birthday', '<', '2000-10-1')->select(); // 小于某个时间 whereTime('birthday', 'between', ['1970-10-1', '2000-10-1'])->select(); // 时间区间查询 whereTime('birthday', 'not between', ['1970-10-1', '2000-10-1'])->select(); // 不在某个时间区间三、时间表达式 // 获取今天的文章 Db::table('think_news')->whereTime('create_time', 'today')->select(); // 获取昨天的文章 Db::table('think_news')->whereTime('create_time', 'yesterday')->select(); // 获取本周的文章 Db::table('think_news')->whereTime('create_time', 'week')->select(); // 获取上周的文章 Db::table('think_news')->whereTime('create_time', 'last week')->select(); // 获取本月的文章 Db::table('think_news')->whereTime('create_time', 'month')->select(); // 获取上月的文章 Db::table('think_news')->whereTime('create_time', 'last month')->select(); // 获取今年的文章 Db::table('think_news')->whereTime('create_time', 'year')->select(); // 获取去年的文章 Db::table('think_news')->whereTime('create_time', 'last year')->select();四、如果查询当天、本周、本月和今年的时间,还可以简化为: // 获取今天的文章 Db::table('think_news')->whereTime('create_time', 'd')->select(); // 获取本周的文章 Db::table('think_news')->whereTime('create_time', 'w')->select(); // 获取本月的文章 Db::table('think_news')->whereTime('create_time', 'm')->select(); // 获取今年的文章 Db::table('think_news')->whereTime('create_time', 'y')->select();五、时间范围查询 // 查询两个小时内的文章 Db::table('think_news')->whereTime('create_time', '-2 hours')->select(); // 查询两天内的文章 Db::table('think_news')->whereTime('create_time', '-2 days')->select();
技术教程
# PHP
# MySQL
易航
3年前
0
438
2
2022-09-06
初探PHP-Parser和PHP代码混淆
初探PHP-Parser PHP-Parser 是 nikic 用PHP编写的PHP5.2到PHP7.4解析器,其目的是简化静态代码分析和操作。 解析 创建一个解析器实例: use PhpParser\ParserFactory; $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);ParserFactory接收以下几个参数: ParserFactory::PREFER_PHP7 :优先解析PHP7,如果PHP7解析失败则将脚本解析成PHP5 ParserFactory::PREFER_PHP5 :优先解析PHP5,如果PHP5解析失败则将脚本解析成PHP7 ParserFactory::ONLY_PHP7 :只解析成PHP7 ParserFactory::ONLY_PHP5 :只解析成PHP5 将PHP脚本解析成抽象语法树(AST) <?php use PhpParser\Error; use PhpParser\ParserFactory; require 'vendor/autoload.php'; $code = file_get_contents("./test.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}"; } var_dump($ast);节点转储 如果是用上面的 var_dump 的话显示的 AST 可能会比较乱,那么我们可以使用 NodeDumper 生成一个更加直观的 AST <?php use PhpParser\NodeDumper; $nodeDumper = new NodeDumper; echo $nodeDumper->dump($stmts);或者我们使用 vendor/bin/php-parse 也是一样的效果 λ vendor/bin/php-parse test.php ====> File test.php: ==> Node dump: array( 0: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: a ) expr: Scalar_LNumber( value: 1 ) ) ) )节点树结构 PHP是一个成熟的脚本语言,它大约有140个不同的节点。但是为了方便使用,将他们分为三类: PhpParser\Node\Stmts 是语句节点,即不返回值且不能出现在表达式中的语言构造。例如,类定义是一个语句,它不返回值,你不能编写类似 func(class {}) 的语句。 PhpParser\Node\expr 是表达式节点,即返回值的语言构造,因此可以出现在其他表达式中。如:$var (PhpParser\Node\Expr\Variable)和func() (PhpParser\Node\Expr\FuncCall)。 PhpParser\Node\Scalars 是表示标量值的节点,如"string" (PhpParser\Node\scalar\string)、0 (PhpParser\Node\scalar\LNumber) 或魔术常量,如"FILE" (PhpParser\Node\scalar\MagicConst\FILE) 。所有 PhpParser\Node\scalar 都是延伸自 PhpParser\Node\Expr ,因为 scalar 也是表达式。 需要注意的是 PhpParser\Node\Name 和 PhpParser\Node\Arg 不在以上的节点之中 格式化打印 使用 PhpParser\PrettyPrinter 格式化代码 <?php use PhpParser\Error; use PhpParser\ParserFactory; use PhpParser\PrettyPrinter; require 'vendor/autoload.php'; $code = file_get_contents('./index.php'); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}"; return; } $prettyPrinter = new PrettyPrinter\Standard; $prettyCode = $prettyPrinter->prettyPrintFile($ast); echo $prettyCode;节点遍历 使用 PhpParser\NodeTraverser 我们可以遍历每一个节点,举几个简单的例子:解析php中的所有字符串,并输出 <?php use PhpParser\Error; use PhpParser\ParserFactory; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; use PhpParser\Node; require 'vendor/autoload.php'; class MyVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ($node instanceof Node\Scalar\String_) { echo $node->value; } } } $code = file_get_contents("./test.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); $traverser = new NodeTraverser; $traverser->addVisitor(new MyVisitor); try { $ast = $parser->parse($code); $stmts = $traverser->traverse($ast); } catch (Error $error) { echo "Parse error: {$error->getMessage()}"; return; }遍历php中出现的函数以及类中的成员方法 <?php class MyVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ( $node instanceof Node\Expr\FuncCall || $node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Stmt\Function_ || $node instanceof Node\Expr\MethodCall ) { echo $node->name; } } }替换php脚本中函数以及类的成员方法函数名为小写 <?php class MyVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ($node instanceof Node\Expr\FuncCall) { $node->name->parts[0] = strtolower($node->name->parts[0]); } elseif ($node instanceof Node\Stmt\ClassMethod) { $node->name->name = strtolower($node->name->name); } elseif ($node instanceof Node\Stmt\Function_) { $node->name->name = strtolower($node->name->name); } elseif ($node instanceof Node\Expr\MethodCall) { $node->name->name = strtolower($node->name->name); } } }需要注意的是所有的 visitors 都必须实现 PhpParser\NodeVisitor 接口,该接口定义了如下4个方法: public function beforeTraverse(array $nodes); public function enterNode(\PhpParser\Node $node); public function leaveNode(\PhpParser\Node $node); public function afterTraverse(array $nodes); beforeTraverse 方法在遍历开始之前调用一次,并将其传递给调用遍历器的节点。此方法可用于在遍历之前重置值或准备遍历树。 afterTraverse 方法与 beforeTraverse 方法类似,唯一的区别是它只在遍历之后调用一次。 在每个节点上都调用 enterNode 和 leaveNode 方法,前者在它被输入时,即在它的子节点被遍历之前,后者在它被离开时。 这四个方法要么返回更改的节点,要么根本不返回(即null),在这种情况下,当前节点不更改。 PHP代码混淆 下面举两个php混淆的例子,比较简单(郑老板@zsx所说的20分钟内能解密出来的那种),主要是加深一下我们对 PhpParser 使用 phpjiami 大部分混淆都会把代码格式搞得很乱,用 PhpParser\PrettyPrinter 格式化一下代码 <?php use PhpParser\Error; use PhpParser\ParserFactory; use PhpParser\PrettyPrinter; require 'vendor/autoload.php'; $code = file_get_contents('./test.php'); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } $prettyPrinter = new PrettyPrinter\Standard; $prettyCode = $prettyPrinter->prettyPrintFile($ast); file_put_contents('en_test.php', $prettyCode);格式基本能看了 图片 因为函数和变量的乱码让我们之后的调试比较难受,所以简单替换一下混淆的函数和变量 <?php use PhpParser\Error; use PhpParser\NodeFinder; use PhpParser\ParserFactory; require 'vendor/autoload.php'; $code = file_get_contents("./index_1.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); $nodeFinder = new NodeFinder; try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } $Funcs = $nodeFinder->findInstanceOf($ast, PhpParser\Node\Stmt\Function_::class); $map = []; $v = 0; foreach ($Funcs as $func) { $funcname = $func->name->name; if (!isset($map[$funcname])) { if (!preg_match('/^[a-z0-9A-Z_]+$/', $funcname)) { $code = str_replace($funcname, "func" . $v, $code); $v++; $map[$funcname] = $v; } } } $v = 0; $map = []; $tokens = token_get_all($code); foreach ($tokens as $token) { if ($token[0] === T_VARIABLE) { if (!isset($map[$token[1]])) { if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) { $code = str_replace($token[1], '$v' . $v++, $code); $map[$token[1]] = $v; } } } } file_put_contents("index_2.php", $code);变量和函数基本能看了,还是有一些数据是乱码,这个是它自定义函数加密的字符串,大多数都是php内置的函数,我们调试一下就基本能看到了 图片 但是得注意一下,phpjiami有几个反调试的地,在35行的地方打个断点 图片 可以看到4个反调试的点: 第一个点: 当你是以cli运行php的时候就会直接 die() 掉,直接注释掉即可 php_sapi_name()=="cli" ? die() : '';第二个点: 和第一个差不多,也是验证运行环境的,cli模式下没有这些变量索引,直接注释即可 if (!isset($_SERVER["HTTP_HOST"]) && !isset($_SERVER["SERVER_ADDR"]) && !isset($_SERVER["REMOTE_ADDR"])) { die(); }第三个点: 如果你在 if 语句处停留超过100ms的话就会直接 die 掉,注释即可 $v46 = microtime(true) * 1000; eval(""); if (microtime(true) * 1000 - $v46 > 100) { die(); }第四个点: $51就是整个文件内容,这行是用于加密前的文件对比是否完整,如果不完整则执行$52(),因为$52不存在所以会直接报错退出,而如果对比是完整的话那么就是$53,虽然$53也不存在,但只是抛出一个Warning,所以我们这里也是直接把这行注释掉。 !strpos(func2(substr($v51, func2("???"), func2("???"))), md5(substr($51, func2("??"), func2("???")))) ? $52() : $53;注释完之后我们在return那里打一个断点,可以发现在return那里我们需要解密的文件内容呈现了出来。 图片 解密之后的内容: <?php @eval("//Encode by phpjiami.com,Free user."); // Clear the uploads directory every hour highlight_file(__FILE__); $sandbox = "uploads/" . md5("De1CTF2020" . $_SERVER['REMOTE_ADDR']); @mkdir($sandbox); @chdir($sandbox); if ($_POST["submit"]) { if (($_FILES["file"]["size"] < 2048) && Check()) { if ($_FILES["file"]["error"] > 0) { die($_FILES["file"]["error"]); } else { $filename = md5($_SERVER['REMOTE_ADDR']) . "_" . $_FILES["file"]["name"]; move_uploaded_file($_FILES["file"]["tmp_name"], $filename); echo "save in:" . $sandbox . "/" . $filename; } } else { echo "Not Allow!"; } } function Check() { $BlackExts = array("php"); $ext = explode(".", $_FILES["file"]["name"]); $exts = trim(end($ext)); $file_content = file_get_contents($_FILES["file"]["tmp_name"]); if ( !preg_match('/[a-z0-9;~^`&|]/is', $file_content) && !in_array($exts, $BlackExts) && !preg_match('/\.\./', $_FILES["file"]["name"]) ) { return true; } return false; } ?> <html> <head> <meta charset="utf-8"> <title>upload</title> </head> <body> <form action="index.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" id="file"><br> <input type="submit" name="submit" value="submit"> </form> </body> </html>其实phpjiami加密之后的整个脚本就是解密我们文件的脚本,我们的文件内容被加密之后放在了 ?> 最后面 图片 整个解密过程也比较简单,其中$v51是我们加密之后内容,$v55是解密后的内容。 $v55 = str_rot13(@gzuncompress(func2(substr($v51,-974,$v55))));其中func2是解密函数 图片 最后是拿func2解密之后的代码放在这个 eval 中执行 图片 还有一种比较简单快捷的方法是通过 hook eval 去获取 eval 的参数,因为不涉及 PHP-Parser 所以就不过多展开了。 Enphp混淆 官网:http://enphp.djunny.com/ github: https://github.com/djunny/enphp 使用官方的加密例子: <?php include './func_v2.php'; $options = array( //混淆方法名 1=字母混淆 2=乱码混淆 'ob_function' => 2, //混淆函数产生变量最大长度 'ob_function_length' => 3, //混淆函数调用 1=混淆 0=不混淆 或者 array('eval', 'strpos') 为混淆指定方法 'ob_call' => 1, //随机插入乱码 'insert_mess' => 0, //混淆函数调用变量产生模式 1=字母混淆 2=乱码混淆 'encode_call' => 2, //混淆class 'ob_class' => 0, //混淆变量 方法参数 1=字母混淆 2=乱码混淆 'encode_var' => 2, //混淆变量最大长度 'encode_var_length' => 5, //混淆字符串常量 1=字母混淆 2=乱码混淆 'encode_str' => 2, //混淆字符串常量变量最大长度 'encode_str_length' => 3, // 混淆html 1=混淆 0=不混淆 'encode_html' => 2, // 混淆数字 1=混淆为0x00a 0=不混淆 'encode_number' => 1, // 混淆的字符串 以 gzencode 形式压缩 1=压缩 0=不压缩 'encode_gz' => 0, // 加换行(增加可阅读性) 'new_line' => 1, // 移除注释 1=移除 0=保留 'remove_comment' => 1, // debug 'debug' => 1, // 重复加密次数,加密次数越多反编译可能性越小,但性能会成倍降低 'deep' => 1, // PHP 版本 'php' => 7, ); $file = 'test.php'; $target_file = 'en_test.php'; enphp_file($file, $target_file, $options);加密之后大概长这样子 图片 可以看到,我们的大部分字符串、函数等等都被替换成了类似于 $GLOBALS{乱码}[num] 这种形式,我们将其输出看一下: 图片 可以看到我们原本的脚本中的字符串都在此数组里面,所以我们只要将$GLOBALS{乱码}[num]还原成原来对应的字符串即可。 那么我们如何获取 $GLOBALS{乱码} 数组的内容,很简单,在我们获取AST节点处打断点即可找到相关内容: 图片 $split = $ast[2]->expr->expr->args[0]->value->value; $all = $ast[2]->expr->expr->args[1]->value->value; $str = explode($split, $all); var_dump($str);可以看到,和上面输出的是一样的(如果加密等级不一样则还需要加一层 gzinflate ) 图片 然后就是通过AST一个节点一个节点将其替换即可,如果不知道节点类型的同学可以用 $GLOBALS[A][1] ,将其输出出来看一下即可,然后根据节点的类型和数据进行判断即可,如下: class PhpParser\Node\Expr\ArrayDimFetch#1104 (3) { public $var => class PhpParser\Node\Expr\ArrayDimFetch#1102 (3) { public $var => class PhpParser\Node\Expr\Variable#1099 (2) { public $name => string(7) "GLOBALS" protected $attributes => array(2) { ... } } public $dim => class PhpParser\Node\Expr\ConstFetch#1101 (2) { public $name => class PhpParser\Node\Name#1100 (2) { ... } protected $attributes => array(2) { ... } } protected $attributes => array(2) { 'startLine' => int(2) 'endLine' => int(2) } } public $dim => class PhpParser\Node\Scalar\LNumber#1103 (2) { public $value => int(1) protected $attributes => array(3) { 'startLine' => int(2) 'endLine' => int(2) 'kind' => int(10) } } protected $attributes => array(2) { 'startLine' => int(2) 'endLine' => int(2) } }根据上面的节点编写脚本 public function leaveNode(Node $node) { if ( $node instanceof PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof PhpParser\Node\Expr\ArrayDimFetch && $node->var->var instanceof PhpParser\Node\Expr\Variable && $node->var->var->name === "GLOBALS" && $node->var->dim instanceof PhpParser\Node\Expr\ConstFetch && $node->var->dim->name instanceof PhpParser\Node\Name && $node->var->dim->name->parts[0] === $this->str && $node->dim instanceof PhpParser\Node\Scalar\LNumber ) { return new PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); } return null; }解出来的内容如下,可以看到大部分已经成功解密出来了 图片 还有就是解密的一部分出现这样语句:('highlight_file')(__FILE__); ,很明显不符合我们平时的写法,将其节点重命名一下 if ( ($node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\MethodCall) && $node->name instanceof Node\Scalar\String_ ) { $node->name = new Node\Name($node->name->value); }现在看起来就舒服多了 图片 我们分析剩下乱码的部分 图片 可以看到是函数里面的局部变量还是乱码,从第一句可以看出所有的局部变量都是以 & $GLOBALS[乱码] 为基础的,而 & $GLOBALS[乱码] 是我们上面已经找出来的,所以也是将其替换即可。 if ( $node instanceof \PhpParser\Node\Stmt\Expression && $node->expr instanceof \PhpParser\Node\Expr\AssignRef && $node->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->expr->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr->var->name === "GLOBALS" && $node->expr->expr->dim instanceof \PhpParser\Node\Expr\ConstFetch && $node->expr->expr->dim->name instanceof \PhpParser\Node\Name && $node->expr->expr->dim->name->parts != [] ) { $this->Localvar = $node->expr->var->name; return NodeTraverser::REMOVE_NODE; } else if ( $node instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof \PhpParser\Node\Expr\Variable && $node->var->name === $this->Localvar && $node->dim instanceof \PhpParser\Node\Scalar\LNumber ) { return new \PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); }替换之后,可以看到与 & $GLOBALS[乱码] 有关的已经全部被替换了,只有变量部分是乱码了 替换变量为 $v 这种形式 <?php function BeautifyVariables($code) { $v = 0; $map = []; $tokens = token_get_all($code); foreach ($tokens as $token) { if ($token[0] === T_VARIABLE) { if (!isset($map[$token[1]])) { if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) { $code = str_replace($token[1], '$v' . $v++, $code); $map[$token[1]] = $v; } } } } return $code; }至此所有代码全部被还原(除了变量名这种不可抗拒因素之外) 图片 还有一部分是没有用的全局变量和常量,手动或者根据AST去进行删除即可,下面贴一下完整解密脚本 <?php require "./vendor/autoload.php"; use \PhpParser\Error; use \PhpParser\ParserFactory; use \PhpParser\NodeTraverser; use \PhpParser\NodeVisitorAbstract; use \PhpParser\Node; use \PhpParser\PrettyPrinter\Standard; class MyVisitor extends NodeVisitorAbstract { public $str; public $str_arr; public $Localvar; public function leaveNode(Node $node) { if ( $node instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var->var instanceof \PhpParser\Node\Expr\Variable && $node->var->var->name === "GLOBALS" && $node->var->dim instanceof \PhpParser\Node\Expr\ConstFetch && $node->var->dim->name instanceof \PhpParser\Node\Name && $node->var->dim->name->parts[0] === $this->str && $node->dim instanceof \PhpParser\Node\Scalar\LNumber ) { return new \PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); } if (($node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\MethodCall) && $node->name instanceof Node\Scalar\String_ ) { $node->name = new Node\Name($node->name->value); } if ( $node instanceof \PhpParser\Node\Stmt\Expression && $node->expr instanceof \PhpParser\Node\Expr\AssignRef && $node->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->expr->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr->var->name === "GLOBALS" && $node->expr->expr->dim instanceof \PhpParser\Node\Expr\ConstFetch && $node->expr->expr->dim->name instanceof \PhpParser\Node\Name && $node->expr->expr->dim->name->parts != [] ) { $this->Localvar = $node->expr->var->name; return NodeTraverser::REMOVE_NODE; } else if ( $node instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof \PhpParser\Node\Expr\Variable && $node->var->name === $this->Localvar && $node->dim instanceof \PhpParser\Node\Scalar\LNumber ) { return new \PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); } return null; } } function BeautifyVariables($code) { $v = 0; $map = []; $tokens = token_get_all($code); foreach ($tokens as $token) { if ($token[0] === T_VARIABLE) { if (!isset($map[$token[1]])) { if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) { $code = str_replace($token[1], '$v' . $v++, $code); $map[$token[1]] = $v; } } } } return $code; } $code = file_get_contents("./en_test.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } var_dump($ast); $split = $ast[2]->expr->expr->args[0]->value->value; $all = $ast[2]->expr->expr->args[1]->value->value; $str1 = $ast[2]->expr->var->dim->name->parts[0]; $str_arr = explode($split, $all); $visitor = new MyVisitor; $visitor->str = $str1; $visitor->str_arr = $str_arr; $traverser = new NodeTraverser; $traverser->addVisitor($visitor); $stmts = $traverser->traverse($ast); $prettyPrinter = new Standard; $code = $prettyPrinter->prettyPrintFile($stmts); $code = BeautifyVariables($code); echo $code;注:需要注意的是 enphp 使用的全局变量不一定是 GLOBALS ,也可能是 _SERVER、_GET 等等,根据具体情况进行判断,还有就是加密等级不同对应的解密方式也是不同的,不过其中的思想都是大同小异 参考 https://www.52pojie.cn/thread-693641-1-1.html https://www.52pojie.cn/thread-883976-1-1.html https://xz.aliyun.com/t/7363 https://github.com/nikic/PHP-Parser/blob/master/doc/2_Usage_of_basic_components.markdown
技术教程
# PHP
易航
3年前
1
304
1
2022-09-06
关于PHP初级闭包(Closure)
匿名函数 提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它。声明一个匿名函数是这样: $func = function() { }; //带结束符可以看到,匿名函数因为没有名字,如果要使用它,需要将其返回给一个变量。匿名函数也像普通函数一样可以声明参数,调用方法也相同: $func = function($param) { echo $param; }; $func('易航博客'); //输出:易航博客顺便提一下,PHP在引入闭包之前,也有一个可以创建匿名函数的函数: create function ,但是代码逻辑只能写成字符串,这样看起来很晦涩并且不好维护,所以很少有人用。 实现闭包 将匿名函数在普通函数中当做参数传入,也可以被返回。这就实现了一个简单的闭包。 下边有三个例子 例一:在函数里定义一个匿名函数,并且调用它 function printStr() { $func = function ($str) { echo $str; }; $func('易航博客'); } printStr();例二:在函数中把匿名函数返回,并且调用它 function getPrintStrFunc() { $func = function ($str) { echo $str; }; return $func; } $printStrFunc = getPrintStrFunc(); $printStrFunc('易航博客');例三:把匿名函数当做参数传递,并且调用它 function callFunc($func) { $func('易航博客'); } // 也可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉 callFunc(function( $str ) { echo $str; });连接闭包和外界变量的关键字:USE 闭包可以保存所在代码块上下文的一些变量和值。PHP在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用 use 关键字。 换一个例子看看: function getMoney() { $rmb = 1; $dollar = 6; $func = function () use ($rmb) { echo $rmb; echo $dollar; }; $func(); } getMoney(); //输出: //1 //报错,找不到dorllar变量可以看到,dollar 没有在 use 关键字中声明,在这个匿名函数里也就不能获取到它,所以开发中要注意这个问题。 有人可能会想到,是否可以在匿名函数中改变上下文的变量,但我发现是不可以的: function getMoney() { $rmb = 1; $func = function () use ($rmb) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney(); // 输出: // 1 // 1所以 use 所引用的也只不过是变量的一个副本而已。但是我想要完全引用变量,而不是复制。 要达到这种效果,其实在变量前加上 & 符号就可以了: function getMoney() { $rmb = 1; $func = function () use (&$rmb) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney(); // 输出: // 1 // 2这样匿名函数就可以引用上下文的变量了。如果将匿名函数返回给外界,匿名函数会保存 use 所引用的变量,而外界则不能得到这些变量,这样形成‘闭包’这个概念可能会更清晰一些。 根据描述改变一下上面的例子: function getMoneyFunc() { $rmb = 1; $func = function () use (&$rmb) { echo $rmb; //把$rmb的值加1 $rmb++; }; return $func; } $getMoney = getMoneyFunc(); $getMoney(); $getMoney(); $getMoney(); // 输出: // 1 // 2 // 3总结 PHP闭包的特性并没有太大惊喜,其实用 class 就可以实现类似甚至强大得多的功能,更不能和js的闭包相提并论,只能期待PHP以后对闭包支持的改进。不过匿名函数还是挺有用的,比如在使用 preg_replace_callback 等之类的函数可以不用在外部声明回调函数了。
技术教程
# PHP
易航
3年前
0
68
1
2022-08-19
被“嫌弃”的PHP:未来仍光明
即使面临各种新技术的挑战,PHP 的未来仍然光明。多年来,在 Web 开发社区内部形成了一种厌恶 PHP 的气氛。现如今,厌恶 PHP 和赞美新技术(如 Node)几乎成了一种奇想。特别是在年轻的社区,PHP 被认为是一只“恐龙”。 PHP 是一门伟大的编程语言。 它并不完美,有利有弊,但总的来说,如果你从事 Web 开发工作,你最好试着去理解它,而不是跟风去厌恶它…… 你甚至可以从中学到一些东西! 图片 我们来看看 PHP 和 Node 之间的区别,这些区别让很多人留在了 PHP 平台,也让其他人转向了不同的编程语言! 社区 PHP 社区比 Node 社区大。 NPM/Node 社区也很大,但缺乏能够真正维护好和做好 Node 包的人。 很多模块被弃用或不再更新。最糟糕的是,大多数模块依赖了其他大量的模块,这意味着如果你使用的模块依赖了一个包含漏洞的旧模块,你可能不知情,或者需要花很多时间自己去更新所有的东西。 这很重要,因为不管一门编程语言或一个框架有多好,如果没有人维护,或者如果没有关于它的讨论、PR或者开源项目,它最终就会消亡。 PHP 生态系统为你提供了大量的库:JWT 身份验证、生成 Excel 电子表格和 PDF、缓存管理、ORM 框架……这些库被广泛使用,具有很好的安全性,且提供了良好的文档。 Symfony 框架也提供了一些官方文档,比如 LexikJWTAuthentication! 事实上,大约 80% 的 Web 应用使用 PHP 开发。 框架 Symfony 和 Laravel 这两个主要的 PHP 框架现在是 Web 的一个巨大组成部分。Laravel 在美国很受欢迎,Symfony 在欧洲很受欢迎,如果我们把 WordPress 去掉,这两个框架占了 PHP 生态系统的 90% 以上。 这些框架比大多数 Node 的框架都要老,并且比现在的 Node 框架拥有更广泛的包和文档生态系统。 在使用 Symfony 过程中遇到了问题?版本 3?版本 4?这都不是事!大量的 StackOverflow 帖子、Medium 文章、官方文档可供你参考。 Symfony 和 Laravel 也提供了一些“基本的项目结构”,你当然可以不用它,你可以按照你想要的方式构建你的项目,但这些基本模式通常适用于多种类型的应用。 在大多数情况下,我们可以通过配置对它们进行调整,以满足我们的各种需求。由于这些框架已经推出了好几年,你可能想到的大多数有用的特性都已经有了,因为在你之前的那些开发人员也有与你同样的需求。 我们以 Express 为例,它是 Node 最著名的框架,主要用于编写 API,它并没有提供强制的结构。这意味着一个没有经验的开发人员更有可能构建出一些不符合标准的东西,而 PHP 框架在这方面的风险要小得多。 性能 Node 的速度很快,在某些情况下比 PHP 更快,但 PHP 也不是太糟糕。 PHP 8.1 借助 OPCache 和 JIT 编译获得更快的执行速度。 Node 在速度方面利用了它的异步特性,但它是单线程的。PHP 利用了在多个线程上运行的优势,而且是同步的。 事实上,现在服务器的价格一般都不会很高,伸缩一个 Web 应用从未像今天这样容易。对于小型 Web 应用来说,鉴于如今的计算能力,性能不再是一个值得花太多时间去争论的点。 然而,对于大规模的应用程序,价格可能是一个关注点。 这就是为什么把常用的 PHP-FPM/Nginx 栈换成 Swoole 会是一个不错的选择。 我曾经看到过一些 PHP 应用程序将 Swoole 作为底层的 HTTP 服务器,在性能上击败了 Node! 此外,使用消息队列是平衡应用程序工作负载的一个很好的方法,这可以很容易地使用 PHP 和 Node 来实现。 易用性 虽然 Node/Express 经常被用来编写 API,并与使用 React/Angular/Vue 等框架构建的前端通信,但大多数 PHP 框架都采用了 MVC 模式。 MVC 即模型视图控制器。一张图片胜过长篇大论:如果你不了解 MVC,这里有一张图可以帮你快速理解 MVC 模式。 图片 构建一个前后端分离的应用程序通常比使用支持前后端技术栈的框架要慢。事实上,许多后端开发人员知道如何编写出色的 HTML/CSS,但不熟悉 React 或其他框架的概念或语法。 结论 PHP 和 Node 各有优点和缺点。 如果你需要稳定性、可靠性和长期支持,我建议使用 PHP。 这些框架成熟且安全,在我看来是首选。 不过,对于需要高吞吐量和实时数据处理的 API 来说,Node 是一个不错的选择。另外,有一些项目不能用 PHP 来完成,比如 Discord 机器人(尽管可能可以用 PHP 来实现,但已经有一个官方的 JavaScript 库……) 有时候,用另一种编程语言来开发应用程序也是不错的,我们可以从中发现一些新的概念或做事的方式,然后将它们应用到其他编程语言中。
技术教程
# PHP
易航
3年前
1
158
6
2022-08-19
PHP 8.2将在今年内发布,一起来看看都有什么新特征
PHP 8.2预计将于今年11月发布,最新的稳定版本是PHP 8.1.5。虽然现在还为时过早,但对更新的接受程度参差不齐。 但是,知道会发生什么可以帮助您为最新的PHP版本做好准备。通过了解新功能和不推荐使用的功能,您可以了解更新可能如何影响开发。这些知识还可以帮助您为最终发布做好准备。 在这篇文章中,我们将回顾最新的PHP版本。然后我们将介绍PHP 8.2中的新功能并讨论发布时间表。 PHP版本概述 PHP 7.4引入了类型化属性、下划线数字分隔符和各种改进。从那时起,已经发布了更多的PHP迭代。 2020年11月发布的PHP 8.0带来了一些基本功能。除了语法和性能增强之外,该版本还包括: 命名参数 匹配语法 Union类型 Constructor Property Promotion JIT(影响PHP执行源代码的方式) 一年后出现了PHP 8.1,这是最新的主要PHP版本。此更新包括重要功能,例如: Intersection类型 只读属性 Enums Fibers 从不返回类型 掌握最新版本的PHP有助于提高网站的性能和安全性。但是,重要的是要知道在升级之前会发生哪些变化。如果您有兴趣测试PHP 8.2的当前状态,可以通过GitHub进行。 PHP 8.2中的新功能 PHP 8.2预计将于2022年底发布。这是当前的发布时间表,计划于2022年11月24日发布通用版本 (GA): 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 根据PHP网站上的官方文档,应该有一些新特性和不推荐使用的功能。 新的 memory_reset_peak_usage 函数 PHP 8.2将包含一个名为 memory_reset_peak_usage 的新函数。它将重置 memory_get_peak_usage 函数返回的峰值内存使用量。 对于涉及多次调用操作并记录每次迭代的峰值内存使用情况的情况,此功能将很有帮助。开发人员将能够使用此新功能在请求的生命周期内的任何给定时间重置峰值内存使用量。 只读类 在PHP 8.1中引入,只读属性将在PHP 8.2中扩展以添加语法糖,以便所有类属性一次都是只读的: readonly class Post { public function __construct( public string $title, public Author $author, public string $body, public DateTime $publishedAt, ) { } }这将防止将动态属性添加到类中。 Null 和 False 独立类型 在PHP 8.2中,false 的返回类型将作为独立类型使用,而不是严格的联合类型,用于发生错误时,这已经是可能的: function alwaysFalse(): false { return false; }null 类型也是如此。例如,作为独立类型,与以前不同,NullPost::getAuthor() 将只能返回 null 。 弃用动态属性 动态属性将在PHP 8.2中被弃用,导致PHP 9.0出现 ErrorException。这些属性是在对象上设置的: class Post { public string $title; } // … $post->name = 'Name';动态属性允许在没有严格的类声明的情况下创建类(例如,值对象)时具有灵活性。对于依赖动态属性的开发人员来说,他们的弃用可能会成为一个问题,因为这会促使他们更多地进行静态分析。出于这个原因,一些开发人员对PHP 8.2的这种变化感到担忧。 但是,使用 __get 和 __set 的类仍将支持动态属性,stdClass 的对象也将如此。 或者,开发人员可以在这些属性的类上使用在全局命名空间中声明的新 #[AllowDynamicProperties]attribute: # [AllowDynamicProperties] class User{} $user = new User(); $user->foo = 'bar';虽然不建议这样做,但另一种选择是禁用弃用警告。 新的 /n 修饰符 PHP 8.2将包含对 preg_* 函数系列的 /n (no capture) modifier的支持。使用时,除了已命名的捕获组之外,任何具有()meta-characters的组都不会捕获。 本质上,结果与将每个组标记为非捕获相同。 此更改背后的原因是修饰符简化了多个组的复杂正则表达式。开发人员可以将所有组标记为非捕获,而不是将每个组都营销为非捕获。 然后,他们可以选择并命名需要捕获的特定组。 在回溯中编辑参数 许多开发人员使用从代码库跟踪堆栈跟踪和生产错误的服务。这些服务可以在出现问题时通知用户。例如,在调试应用程序时跟踪调用堆栈很有帮助。 但是,有时堆栈跟踪可能包含敏感信息,例如用户名和密码。PHP 8.2将包含一个 #[SensitiveParameter] 属性,当出现问题时,该属性将防止此信息包含在堆栈跟踪中: function test($foo, #[\SensitiveParameter] $bar, $baz) { throw new Exception('Error'); } test('foo', 'bar', 'baz');PHP 8.2将使用敏感参数从堆栈跟踪中编辑私有信息,使其更加安全。这些参数确保数据不会出现在错误日志中。请注意,此属性仅可用于参数。 弃用 ${} 字符串插值 有多种方法可以使用PHP在字符串中嵌入变量。但是,PHP 8.2将弃用两种方法。第一个是在字符串中使用 ${}: $str = "Hello ${world}";第二个是使用 ${}(变量) : $str = "Hello ${(world)}";这对开发人员来说可能不是一个重大问题,因为两种最流行的字符串插值方法仍然有效。 弃用部分支持的可调用对象 另一个不推荐使用的更改是部分支持的callables。有多种方法可以在 PHP 中创建可调用对象。可以使用 $callable() 语法、user_call_func(array) 或使用带有回调的函数调用带或不带参数的函数。 已弃用的可调用模式包括以下内容: $callable = "self::method"; $callable = "parent::method"; $callable = "static::method"; $callable = ["self", "method"]; $callable = ["parent", "method"]; $callable = ["static", "method"]; $callable = ["MyClass", "MyParentClass::myMethod"]; $callable = [new MyClass(), "MyOtherClass::myMethod"]从PHP 8.2开始,调用上述任何一个都将导致以下弃用通知: class Test { public static function myMethod(): void { echo "Called"; } public static function call(): void { $callable = 'self::myMethod'; call_user_func($callable); } } $callable = Test::call(); // "Called"但是,将这些可调用对象传递给is_callable函数或将它们与可调用参数类型一起使用不会生成弃用消息。为了防止出现弃用通知,开发人员可以使用::class 魔术方法将可调用代码中的parent、self和static关键字转换为各自的类名。 更改背后的部分原因是允许将可调用对象用于类型化属性。它使它们不那么依赖于上下文。 MySQLi不能再用libmysql编译 过去,PHP支持两个库来连接MySQL数据库:mysqlnd和libmysql。自PHP 5.4起,前者已成为默认库。但是,可以通过扩展编译MySQLi。 从PHP 8.2开始,将不支持使用libmysql编译MySQLi扩展。尝试这样做会导致配置错误: ./configure --with-mysqli=FOO不再支持将mysqli与外部库链接 这不太可能对开发人员造成任何重大错误。但是,通过LDAP和SASL自动重新连接和身份验证支持libmysql支持的两个mysqlnd不可用的最大功能。
技术教程
# PHP
易航
3年前
2
191
1
上一页
1
...
3
4
5
6
下一页