标签 PHP 下的文章 - 易航博客
首页
文章分类
源码资源
技术教程
程序软件
文创娱乐
玄学修炼
关于我们
其他页面
网站统计
友情链接
用户留言
高清壁纸
关于易航
热门文章
Joe再续前缘主题 - 本站同款
易航网址导航系统 – 功能强大,轻量易用
Js自动播放HTML音乐(不受浏览器限制,无需先与浏览器交互,无需对浏览器进行修改)
JsonDb-PHP轻量级文件数据库系统
Typecho一键调整网站为冬天情景插件
标签搜索
PHP
网站源码
Web前端
PHP源码
Typecho
课程资料
HTML源码
Typecho插件
武术内功
Joe主题
Web
Android软件
国漫
网络协议
Windows程序
MySQL
Windows
小说
Linux程序
Java源码
发布
登录
注册
找到
32
篇与
PHP
相关的结果
2024-12-04
PHP 纤程:它真的能实现异步执行吗?
图片图片 PHP 8.1 引入了 Fibers,这让许多开发者好奇:它能否打破 PHP 作为单线程同步语言的固有局限,让它像 JavaScript 和 Node.js 那样实现异步操作?答案并非简单的肯定或否定:Fibers 本身并不提供真正的异步执行,但它为更高效的任务管理提供了强大的机制。下文将深入探讨这一概念。 什么是 PHP 纤程? PHP 纤程是一种协作式多任务处理机制。它允许你暂停和恢复代码的特定部分,而不会阻塞整个 PHP 进程。你可以把纤程想象成一种特殊的函数,它能够将执行控制权交回主程序,并在需要时从之前暂停的地方继续执行。 纤维的主要特点 可启动、暂停和恢复执行。 在单一的 PHP 进程内运行,不涉及多线程。 尤其适用于构建非阻塞代码。 当纤程暂停时会发生什么? 当使用 Fiber::suspend() 暂停纤程时,执行控制权会返回主 PHP 脚本。这意味着: 主程序可以继续执行其他部分。 纤程的执行暂停,直到被 resume() 唤醒。 例如: $fiber = new Fiber(function () { echo "Fiber started\n"; Fiber::suspend(); echo "Fiber resumed\n"; }); echo "Before Fiber\n"; $fiber->start(); echo "After Fiber Start\n"; $fiber->resume(); echo "After Fiber Resume\n";输出: Before Fiber Fiber started After Fiber Start Fiber resumed After Fiber Resume以下是具体情况 1、Fiber::suspend() 暂停纤程,并将执行控制权返回到启动纤程的主脚本 ($fiber->start() 之后的代码)。 2、主脚本继续执行。 3、当调用 resume() 时,纤程从暂停处恢复执行,直至完成。 恢复纤程是否会阻塞主进程? 调用 Fiber::resume() 恢复纤程会暂时阻塞主进程。这意味着: 在纤程完成或再次暂停之前,脚本的其他部分(或其他纤程)无法执行。 由于 PHP 的单线程特性,纤程的执行仍然是阻塞式的。 尽管纤程提供了更灵活的控制,但它并没有改变 PHP 的底层执行模型。 例如: $fiber = new Fiber(function () { echo "Processing Fiber...\n"; sleep(2); // Simulates a blocking task echo "Fiber Done\n"; }); echo "Before Fiber\n"; $fiber->start(); echo "Between Fiber Start and Resume\n"; $fiber->resume(); echo "After Fiber\n";输出: Before Fiber Processing Fiber... Fiber Done Between Fiber Start and Resume After Fiber在此示例中,纤程在调用 sleep(2) 期间会阻塞主进程。 因此,尽管纤程提供了一种更有效地组织代码的方式,但它们并不会神奇地实现并行处理或真正的异步执行。 它们只是提供了一种在单线程环境下更精细地控制执行流程的机制。 光纤如何仍然“不阻塞”? 纤程的“非阻塞”并非指并行执行,而是指其更优的任务管理能力。纤程暂停时不会阻塞主进程,而是将控制权交还给主脚本或事件循环。 这在基于事件驱动的库或框架(如 ReactPHP 或 Amp)中尤为有用: 长时间运行或等待的任务(例如数据库查询、API 调用)可以被暂停。 其他任务可以同时进行。 当任务准备好后,纤程会被恢复并继续执行。 这使得在单线程环境下能够模拟异步行为,从而提升效率。 事件循环和纤程 纤程通常与事件循环结合使用才能实现真正的非阻塞操作。事件循环负责跟踪多个任务并决定下一步执行哪个任务。其工作原理如下: 当遇到阻塞任务(例如数据库查询)时,纤程自动暂停。 事件循环选择并处理其他任务。 阻塞任务完成后,事件循环恢复相应的纤程。 像 Amp 和 ReactPHP 这样的库正是利用纤程和事件循环来实现异步任务处理。 为什么 Fiber 不是真正异步的 与 JavaScript 或 Node.js 中的异步编程(任务可以通过线程或事件循环并行运行)不同,PHP 纤程: 在单个 PHP 进程内同步执行。 通过允许开发者手动控制任务的暂停和恢复来实现协作式多任务处理。 换句话说: 纤程不引入并行性(任务仍然按顺序逐个执行)。 它们是一种更有效地管理和构建非阻塞代码的工具,而非真正的并行机制。 它们提供了一种在单线程环境下模拟异步行为的方式。 尽管 PHP 纤程本身并不能使 PHP 真正实现异步,但它们仍然是该语言的有力补充。原因如下: 1、改进的任务管理:纤程允许暂停和恢复任务,从而释放主进程去处理其他工作。 2、非阻塞工作流: 与事件循环结合使用时,纤程可以实现非阻塞任务处理,提升程序的并发性能。 3、同步执行的便利性: 恢复时,纤程同步执行,简化了代码逻辑,尽管会暂时阻塞进程。 这使得开发者无需处理复杂的回调或 Promise,就能以更线性的方式编写异步代码。 对于需要高效多任务处理的 PHP 应用(例如实时应用、后台作业或 API),纤程是一项重要的改进。它能够更好地管理任务并模拟异步行为。 然而,要实现真正的并行处理,仍然需要依赖外部解决方案,例如扩展或单独的进程来实现多线程。
技术教程
# PHP
易航
2天前
0
5
0
2024-12-03
PHP 8.4 正式发布!🧨
图片 PHP 8.4 第一个正式发布版本今天发布。PHP 8.4 在很大程度上向后兼容 PHP 8.0 到 8.4,并带来了一些新功能。 PHP 8.4 新增/变更功能 curl_getinfo 支持 CURLINFO_POSTTRANSFER_TIME_T curl_version() 支持 feature_list 新增 http_(get|clear)_last_response_headers 函数 新增 CURLOPT_PREREQFUNCTION 选项 新增 CURLOPT_DEBUGFUNCTION 选项 新增 CURLOPT_SERVER_RESPONSE_TIMEOUT 选项,以替换 CURLOPT_FTP_RESPONSE_TIMEOUT 新增 CURLOPT_TCP_KEEPCNT 选项 新增round()函数 新增 mb_trim、mb_ltrim 和 mb_rtrim 函数 DateTime(Immutable) 类新增 createFromTimestamp 方法 phpinfo 显示 PHP 整数大小信息 DateTime(Immutable) 类新增 get/setMicrosecond 方法 新增函数 request_parse_body PCRE2 升级及相关正则表达式变化 PHP 8.4 语法/功能变更 OpenSSL:最低要求的 OpenSSL 版本提升至 1.1.1 MBString:Unicode 字符数据库更新至版本 16 exit/die 从语言构造变为函数 CURLOPT_DNS_USE_GLOBAL_CACHE 不再有效 密码哈希:默认 Bcrypt 成本从 10 变为 12 PHP_ZTS 和 PHP_DEBUG 常量值类型从 int 变为 bool Opcache:JIT 启用方式的 INI 变更 round() 函数:无效的舍入模式将抛出 \ValueError 异常 Curl:最低要求的 libcurl 版本提升至 7.61.0 PHP 8.4 弃用功能 隐式可为空的参数声明被弃用 E_STRICT 常量被弃用 调用 session_set_save_handler() 时使用超过 2 个参数被弃用 CSV:必须提供 $escape 参数 CURLOPT_BINARYTRANSFER 被弃用 PHP 8.4 移除功能 Pspell 扩展从 PHP 核心移至 PECL IMAP 扩展从 PHP 核心移至 PECL OCI8 和 PDO-OCI 扩展从 PHP 核心移至 PECL
技术教程
# PHP
易航
3天前
0
20
0
2024-11-30
PHP 8.4 新功能
图片图片 PHP 8.4 的正式发布计划于下周,即 2024 年 11 月 21 日发布。在此次发布之前,一系列预发布版本(Alpha、Beta 和候选版本)允许社区测试新功能并进行最后一刻的调整。PHP 8.4 引入了多项改进,包括用于操作数组的新功能、受其他语言启发的属性钩子以及简化的语法。让我们一起回顾一下此版本中要记住的新功能。 Property Hooks Property Hooks 是 8.4 版中引入的主要功能之一。PHP 实现的灵感来自其他语言(如 Kotlin、C#、Swift、Javascript 或 Python)中的现有实现。让我们举几个例子来说明 Property Hooks 的用途以及如何使用它们: <?php declare(strict_types=1); class Foo { private string $id; public function getId(): string { return $this->id; } public function setId(string $id): void { $this->id = $id; } }从 PHP 8 开始,由于 Constructor Property Promotion,可以通过以下方式进行简化: <?php class Foo { function __construct(public string $id) {} }在我们必须保留访问器 (getter/setter) 的情况下,我们会考虑包含业务逻辑的丰富模型,我们失去了更轻量级语法的优势。PHP 版本 8.4 和引入的 Property Hooks 修复了这个问题: <?php class Foo { public function __construct( public string $id { get { return '#' . $this->id; } set(string $id){ $this->id = mb_strtoupper($id); } }, ){} }乍一看,语法似乎令人困惑,但就像任何语法演变一样,随着时间的推移,您会习惯它。 Property Hooks 引入的另一种可能性是能够在 property 上定义接口。 使用这个新版本的 PHP,我们可以编写以下定义: <?php interface HasId { public string $id { get; set; } } // Les fonction fléchées peuvent être aussi utilisées pour raccourcir la syntaxe class Foo implements HasId { function __construct( public string $id { get => '#' . $this->id; set (string $id) => $this->id = mb_strtoupper($id); }, ) {} } // Le contrat d’interface est également respecté sans l’utilisation des Property Hooks en déclarant publiquement la propriété class Bar implements HasId { function __construct(public string $id) {} }一个完整的案例 <?php declare(strict_types=1); interface HasId { public string $id { get; set; } } class Foo implements HasId { function __construct( public string $id { get => '#' . $this->id; set (string $id) => $this->id = mb_strtoupper($id); }, ) {} } class Bar implements HasId { function __construct(public string $id) {} } class Baz { public function display(HasId $object): void { echo $object->id . PHP_EOL; } public function update(HasId $object, string $id): void{ $object->id = $id; $this->display($object); } } $foo = new Foo(id: 'FOO'); $bar = new Bar(id: 'BAR'); $baz = new Baz(); $baz->display($foo); $baz->update($foo, 'foo'); $baz->display($bar); $baz->update($bar, 'bar');设置具有非对称可见性的类属性的可见性 非对称可见性 允许您根据相关操作是读取还是写入属性,对同一属性设置不同的可见性。然后,可以定义读取访问的公共可见性和写入访问的更受限的可见性(受保护或私有)。接下来的两个类是等效的,随着非对称可见性的引入,语法更加简洁。 <?php class Foo { function __construct(private string $id,) { // } public function getId(): string { return $this->id; } } class Bar { function __construct( public private(set) string $id, ) {} }不对称可见性附带一些规则,这些规则很容易理解: 属性必须被类型化(来自 PHP 实现的约束) 只关注对象属性,静态属性不能从中受益(这也是 PHP 实现产生的约束)。 对于对象属性,如果该属性设置为 private(set),则不能在与当前类不同的范围内修改链接对象。但是,如果链接对象的属性被定义为 Properties,则可以对其进行修改。 对于数组类型属性,如果该属性已设置为 private(set),则无法在当前类的范围之外操作数组(添加元素、删除元素等)。 set 的可见性不能比 get 的可见性更宽。 对于类继承或接口协定,可见性不能更严格,也可以更广泛。 在 readonly中定义的属性(在 PHP 8.1 中引入)和 public private(set) 中定义的属性之间的差异非常小,但值得一提。上面定义的不对称可见性将具有相同的效果,只是它允许内部更改。换句话说, readonly 限制了 mutation,并且在实例化期间还具有唯一写入的效果。 管独立于 Property Hook 运行,但这两种机制可以结合使用。此处提供了这两种功能的示例。 对惰性对象的原生支持 惰性对象 是其实际实例化将被推迟到实际需要的时间(因为它们的实例化通常很昂贵)的对象。出于性能原因,它们在 Doctrine 和 Symfony 中被大量使用。 Martin Fowler 在他的理论定义中建立了四种可能的实现。其中两个是不需要修改现有对象的实现:Ghost 和 Proxy。这些是通过在 PHP Reflection API 中添加方法保留和访问的。 在这两种情况下,都会创建一个初始化函数。对于 Ghosts,该函数将直接作用于对象。对于 Proxy,它是实例化惰性对象的函数,然后将交互反馈给真实实例。 在这两种情况下,实例化机制都是通过访问真实对象的 state 来触发的:读取或写入属性、测试属性是否具有值、克隆等。可以通过特定函数对特定属性禁用此行为,在某些情况下,可以定义或参数化,例如用于调试或序列化。 由于它是为非常有限且根据定义相当抽象的用例保留的,因此我们邀请您阅读 RFC 以发现代码示例和两种不同实现的详细功能。 不带括号的类实例化 更有趣的是,这种演变通过在实例化新对象时使括号变得多余,从而减轻了语法的负担。 <?php // Avant et toujours valide $o = (new Operation(0))->add(10)->multiply(2); // Depuis PHP 8.4 $o = new Operation(0)->add(10)->multiply(2);解析 HTML5 创建了一个新的 DOM\HTMLDocument 类来允许 HTML5 解析,为了确保与 HTML4 的向后兼容性,当前类将保持不变。这两个类保持相同的 API,因此可以以相同的方式使用。只有构造逻辑已更改,并且需要使用其中一个可用的工厂。用 C 语言编写的底层库是 Lexbor。 新的函数 添加了四个作用于数组的新函数,它们补充了现有函数。 array_find array_find 将返回传递给它的回调函数的第一个匹配项 <?php $array = ['A', 'AA', 'AAA']; $arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3]; array_find($array, static fn(string $value): bool => strlen($value) > 2);// returns AAA array_find($array, static fn(string $value): bool => strlen($value) > 3);// returns null array_find($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns 1array_find_key array_find_key 的工作方式与上一个函数相同,但返回 key 而不是 value: <?php $array = ['A', 'AA', 'AAA']; $arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3]; array_find_key($array, static fn(string $value): bool => strlen($value) > 2); // returns 2 array_find_key($array, static fn(string $value): bool => strlen($value) > 3); // returns null array_find_key($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns Aarray_any 如果数组中至少有一个元素与回调函数匹配,array_any 将返回布尔值 true: <?php $array = ['A', 'AA', 'AAA']; $arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3]; array_any($array, static fn(string $value): bool => strlen($value) > 2)); // returns true array_any($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns truearray_all 如果数组中的所有元素都与回调函数匹配,array_all 将返回布尔值为 true <?php $array = ['A', 'AA', 'AAA']; $arrayWithKeys = ['A' => 1, 'AA' => 2, 'AAA' => 3]; array_all($array, static fn(string $value): bool => strlen($value) < 4)); // returns true array_all($arrayWithKeys, static fn(int $value, string $key): bool => $value === strlen($key)); // returns true改进和错误修复 除其他事项外,我们保留了以下更改: 添加了新的多字节函数来操作字符串 mb_trim、mb_ltrim、mb_rtrim、mb_ucfirst mb_lcfirst。 添加了用于从时间戳创建 DateTime 对象的新函数。 exit 和 die 的元素失去了它们的地位,取而代之的是特殊功能。在不破坏向后兼容性的情况下,这允许对函数进行类似的操作,例如,更精确地键入输入参数。 以下扩展正在从核心中移出以加入 PECL:Pspel、IMAP、OCI8 和 PDO-OCI。 为舍入功能添加了四种新的舍入模式 在 Sodium 中添加了两种新的加密算法,并升级了 OpenSSL。 添加了 cURL 的新选项。 完整的更新日志:https://www.php.net/ChangeLog-8.php
技术教程
# PHP
易航
6天前
0
21
0
2024-11-20
提升PHP技术:18个实用高级特性
掌握PHP基础知识只是第一步。 深入了解这18个强大的PHP特性,将显著提升您的开发效率和代码质量。 超越 __construct() 的魔法方法 虽然 __construct() 为大多数开发者所熟知,PHP 却提供了更多强大的魔术方法,例如: class DataObject { private array $data = []; // 设置不可访问的属性时调用 public function __set($name, $value) { $this->data[$name] = $value; } // 获取不可访问的属性时调用 public function __get($name) { return $this->data[$name] ?? null; } // 对不可访问的属性使用 isset() 时调用 public function __isset($name) { return isset($this->data[$name]); } // 序列化对象时调用 public function __sleep() { return ['data']; } }生成器和收益 使用生成器迭代大型数据集,显著降低内存消耗 function readHugeFile($path) { $handle = fopen($path, 'r'); while (!feof($handle)) { yield trim(fgets($handle)); } fclose($handle); } // 用法 foreach (readHugeFile('large.txt') as $line) { echo $line . PHP_EOL; }匿名类 可以使用匿名类创建无需正式声明的单例实例 $logger = new class { public function log($message) { echo date('Y-m-d H:i:s') . ": $message\n"; } }; $logger->log('发生了一些事');属性(PHP 8+) 代码的元数据注释: #[Route("/api/users", methods: ["GET"])] #[Authentication(required: true)] class UserController { #[Inject] private UserService $userService; #[Cache(ttl: 3600)] public function getUsers(): array { return $this->userService->getAllUsers(); } }纤程并发 PHP 8.1+中的协作式多任务处理: $fiber = new Fiber(function(): void { $value = Fiber::suspend('suspended'); echo "Value: $value\n"; }); $value = $fiber->start(); echo "Fiber suspended with: $value\n"; $fiber->resume('resumed');带有空合并的方法链 优雅地处理可能返回 null 的方法链调用 class User { public function getProfile() { return new Profile(); } } $user = null; $result = $user?->getProfile()?->getName() ?? 'Anonymous';动态属性访问 变量属性和方法名称: class DataAccess { private $name = 'John'; private $age = 30; public function getValue($property) { $getter = 'get' . ucfirst($property); return $this->$getter(); } public function getName() { return $this->name; } }可调用函数和闭包 高级功能处理: $multiply = Closure::bind( function($x) { return $x * $this->multiplier; }, new class { public $multiplier = 2; } ); echo $multiply(5); // 输出: 10特征组成 在类之间复用复杂的业务逻辑 trait Timestampable { private $createdAt; private $updatedAt; public function touch() { $this->updatedAt = new DateTime(); } } trait SoftDeletable { private $deletedAt; public function softDelete() { $this->deletedAt = new DateTime(); } } class User { use Timestampable, SoftDeletable { Timestampable::touch insteadof SoftDeletable; } }命名参数 使用PHP 8更清晰的函数调用: function createUser( string $name, string $email, ?string $role = null, bool $active = true ) { // 实现 } createUser( email: 'john@example.com', name: 'John', active: false );一等可调用函数 PHP 8.1 的简化调用语法: class Math { public function add($a, $b) { return $a + $b; } } $math = new Math(); $add = $math->add(...); echo $add(5, 3); // 输出: 8枚举 PHP 8.1中的类型安全枚举: enum Status: string { case DRAFT = 'draft'; case PUBLISHED = 'published'; case ARCHIVED = 'archived'; public function color(): string { return match($this) { Status::DRAFT => 'gray', Status::PUBLISHED => 'green', Status::ARCHIVED => 'red', }; } }属性类型强制转换 自动类型转换: class Config { private int $timeout = '60'; // 自动将字符串转换为 int private float $rate = '0.5'; // 自动将字符串转换为浮点数 }引用返回值 通过函数返回修改值: class Collection { private array $items = []; public function &getItem($key) { return $this->items[$key]; } } $collection = new Collection(); $item = &$collection->getItem('key'); $item = 'new value'; // 修改原始数组后期静态绑定 静态调用的正确继承: class Parent { public static function who() { return static::class; } } class Child extends Parent { } echo Child::who(); // 输出: Child操作码缓存 通过字节码缓存进行性能优化: // php.ini configuration opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.validate_timestamps=0预加载 永久内存类加载(PHP 7.4+): // preload.php opcache_compile_file(__DIR__ . '/vendor/autoload.php'); opcache_compile_file(__DIR__ . '/app/Models/User.php');反射API 运行时代码检查与修改: class Inspector { public static function getPropertyTypes($class) { $reflection = new ReflectionClass($class); $properties = []; foreach ($reflection->getProperties() as $property) { $type = $property->getType(); $properties[$property->getName()] = $type ? $type->getName() : 'mixed'; } return $properties; } }结论 掌握这些高级PHP特性,将显著提升您的代码质量、开发效率和问题解决能力,从而构建更优雅、高效且易于维护的PHP应用程序。
技术教程
# PHP
易航
11月20日
0
25
0
2024-11-14
PHP函数对象编程 FOP 的替代方案是什么?
函数对象编程(FOP)是一种利用函数作为对象的编程模式。它通常用于定义回调函数,用于在事件触发或其他条件满足时执行特定操作。 虽然 FOP 在某些情况下非常有用,但它也有一些缺点,包括可维护性差、可读性差以及难以调试。 以下是一些函数对象编程的替代方案: 匿名函数 匿名函数是一种没有名称的函数。它们可以使用 lambda 表达式或箭头函数来定义。匿名函数对于快速定义和使用小函数非常有用。 // lambda 表达式 $sum = function(int $a, int $b) { return $a + $b; }; // 箭头函数 $sum = fn(int $a, int $b) => $a + $b;类方法 类方法可以用来定义回调函数。类方法的好处是它们具有更好的可维护性并且更容易调试。 class MyClass { public function sum(int $a, int $b) { return $a + $b; } } $myClass = new MyClass(); $sum = [$myClass, 'sum'];闭包 闭包是一种函数,可以访问它的作用域之外的变量。闭包非常适合在多个函数之间共享状态。 $x = 10; $closure = function() use ($x) { return $x + 2; };实战案例 假设我们想要定义一个回调函数,用于打印字符串。我们可以使用以下方法: 函数对象编程: $callback = new class { public function __invoke(string $message) { echo $message; } }; 匿名函数: $callback = fn(string $message) => echo $message; 闭包: $callback = function(string $message) { echo $message; }; 类方法: class MyClass { public function printMessage(string $message) { echo $message; } } $myClass = new MyClass(); $callback = [$myClass, 'printMessage'];哪种方法最适合取决于具体情况。一般来说,匿名函数和闭包对于小型回调函数是最好的选择,而类方法对于大型回调函数或需要访问外部状态的回调函数是最好的选择。
技术教程
# PHP
易航
11月14日
0
16
0
2024-11-13
探索NativePHP:PHP桌面应用开发的新星
在编程的世界里,PHP一直以其强大的Web开发能力而闻名。然而,随着技术的发展,PHP的应用领域也在不断扩展。今天,我要介绍的是一个令人兴奋的新库——NativePHP,它允许开发者使用PHP语言来编写桌面应用程序。 NativePHP简介 NativePHP是一个新兴的PHP库,它利用PHP的强大功能和易用性,为开发者提供了一种全新的桌面应用开发方式。这个库通过与本地操作系统的交互,使得PHP代码能够直接调用系统级的API,从而实现桌面应用的功能。 图片 入门指导 要开始使用NativePHP,你需要确保你的开发环境已经安装了PHP和Composer。 1. 安装NativePHP:你可以通过Composer来安装NativePHP库。在你的项目目录下运行以下命令: composer require nativephp/php2. 创建你的第一个应用:安装完成后,你可以开始编写你的第一个NativePHP应用。以下是一个简单的Hello World示例: require 'vendor/autoload.php'; use NativePHP\Application; $app = new Application(); $app->run();图片 代码案例 让我们来看一个更复杂的例子,展示如何使用NativePHP创建一个简单的窗口应用: require 'vendor/autoload.php'; use NativePHP\Application; use NativePHP\Window; $app = new Application(); $window = new Window(); $window->setTitle('Hello NativePHP'); $window->setSize(800, 600); $window->show(); $app->run();NativePHP优劣势 优势: 易用性:NativePHP继承了PHP的易用性,使得开发者可以快速上手。 跨平台:NativePHP支持Windows、macOS和Linux,使得你的应用可以跨平台运行。 社区支持:虽然是一个新兴库,但NativePHP已经吸引了一些开发者的关注,社区正在逐渐壮大。 劣势: 性能:与C++或Java等语言编写的桌面应用相比,PHP的性能可能会有所不足。 成熟度:作为一个新兴库,NativePHP可能还缺乏一些成熟的功能和工具。 使用方法和场景 NativePHP适用于那些希望使用PHP来开发桌面应用的开发者。它特别适合以下场景: 快速原型开发:如果你需要快速验证一个想法,NativePHP可以帮助你快速构建一个桌面应用原型。 小型到中型项目:对于不需要极高性能的小型到中型桌面应用,NativePHP是一个不错的选择。 注意事项 在使用NativePHP时,开发者需要注意以下几点: 性能优化:由于PHP的性能限制,开发者需要对应用进行性能优化。 安全性:与Web应用相比,桌面应用可能面临不同的安全挑战,开发者需要特别注意。 结尾 亲爱的开发工程师们,NativePHP为我们打开了一个全新的世界,让我们能够用熟悉的PHP语言来探索桌面应用的开发。不要害怕挑战,勇敢地尝试新事物,你会发现编程的乐趣远不止于此。让我们一起,用代码创造未来! 在这篇文章中,我们深入探讨了NativePHP库,并提供了入门指导、代码案例和使用场景。希望这些信息能帮助你在开发旅程中做出明智的选择,并在技术的海洋中乘风破浪。
技术教程
# PHP
易航
11月13日
0
57
0
2024-11-11
Web 开发 2025: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
易航
11月11日
0
60
0
2024-11-02
轻HTTP - 基于PHP的一款超灵活专业级HTTP轻量网络请求库
简介 轻HTTP请求库是一款专业级轻量的PHP HTTP客户端,用于发送各种HTTP请求。它支持GET、POST、HEAD、DELETE、PUT、PATCH等方法,可以轻松发送JSON、XML等格式的数据。 $client = new Client(); $response = $client->get('http://www.example.com'); echo $response; // 输出响应体该库的主要特性: 支持主流的HTTP方法:GET、POST、HEAD、DELETE、PUT、PATCH 等。可以发送各种请求,获取不同的响应。 支持URL参数、请求体、请求头、Cookie等设置。可以定制灵活的请求参数。 发送请求体支持JSON、XML、文本等格式。支持的请求数据类型丰富。 简单易用,代码量小巧轻量,使用方法灵活。接口简单明了,学习成本低。 基于PHP原生curl扩展,性能高效稳定。利用curl实现,性能优秀。 项目地址:https://gitee.com/yh-it/php-http-request 觉得本项目不错的话可以帮忙点一下星星Star哦 安装 通过 Composer 安装 1. 安装 Composer curl -sS https://getcomposer.org/installer | php2. 运行安装命令 composer require network/http:dev-master3. 启用 Composer 自动加载 require 'vendor/autoload.php';启用后,可直接使用 $client = new Client(); 基本使用 use network\http\Client; $client = new Client(); $client->param('name', '易航'); // 设置请求参数 $client->header('User-Agent', 'Mozilla/5.0'); // 设置请求头 $response = $client->get('http://www.bri6.cn'); // 发送GET请求 echo $response; // 输出响应体请求参数用于构造请求URL的参数,请求头用于定制客户端信息,发起GET请求后获取响应,并输出响应体。 实例化 Client $client = new network\http\Client();param 和 header 方法 可以调用 param() 方法添加URL参数, header() 方法添加请求头。它们既支持单个设置,也支持批量设置: // 单个设置 $client->param('name', '易航'); $client->header('User-Agent', 'Mozilla/5.0'); // 批量设置 $client->param(['name' => '易航', 'age' => 25]); $client->header(['User-Agent' => 'Mozilla/5.0', 'Content-Type' => 'application/json']);发送GET请求 $response = $client->get('http://bri6.cn'); $response = $client->send('http://www.bri6.cn','GET'); $response = $client->modth('GET')->send('http://www.bri6.cn'); $response = $client->url('http://www.bri6.cn')->send();可以传入请求头和查询参数: $response = $client->get('http://bri6.cn', ['name' => '易航'], ['Accept' => 'application/json']);发送POST请求 $response = $client->post('http://bri6.cn', ['name' => '易航']); 可以传入请求体、请求头: $response = $client->post('http://bri6.cn', ['name' => '易航'], ['Content-Type' => 'application/x-www-form-urlencoded']);其他请求方法 delete(): 发送DELETE请求 put(): 发送PUT请求 patch(): 发送PATCH请求 $response = $client->delete('http://www.bri6.cn'); $response = $client->put('http://www.bri6.cn', ['title' => 'Easy']); $response = $client->patch('http://www.bri6.cn', ['views' => 999]);send 方法 send 方法是该库中最为核心和灵活的一个方法,它可以接收三个参数,并可以对这三个参数的顺序进行混合传参。 传统方式 $client->send('http://www.bri6.cn', 'GET', ['name' => '易航']);方法和URL顺序交换 $client->send('GET', 'http://www.bri6.cn', ['name' => '易航']);URL和参数顺序交换 $client->send(['name' => '易航'], 'http://www.bri6.cn', 'GET');链式设置URL和方法,最后传入参数 $client->url('http://www.bri6.cn')->method('GET')->send(['name' => '易航']);链式设置参数,最后传入URL和方法 $client->param(['name' => '易航'])->send('http://www.bri6.cn', 'GET');链式设置方法,最后传入URL和参数 $client->method('GET')->send('http://www.bri6.cn', ['name' => '易航']);send 方法会自动识别第一个参数是 URL、方法还是参数。同时并不强制要求三个参数全部传入,可以只传入一个或两个参数,并可以通过链式调用的方式进行参数设置。 所以根据不同的参数顺序和参数类型,您可以选择一种简洁清晰和习惯的方式进行请求发送。send 方法力求代码的灵活性,同时也不会造成使用上的困扰。 send 方法核心代码: class Client { public function send($param1 = null, $param2 = null, $param3 = null) { $info = $this->_initSend($param1, $param2, $param3); if (!empty($info->method)) $this->method($info->method); if (!empty($info->url)) $this->url($info->url); if ((!empty($info->params)) && is_array($info->params)) $this->param($info->params); $this->_initialize(); $response_body = curl_exec($this->ch); $http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE); $header_size = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE); curl_close($this->ch); $response = $this->response = new Response($http_code, $header_size, $response_body); return $response; } private function _initSend($param1, $param2, $param3) { $url = $this->_isUrl($param1) ?: $this->_isUrl($param2) ?: $this->_isUrl($param3); $method = $this->_isMethod($param1) ?: $this->_isMethod($param2) ?: $this->_isMethod($param3); $params = is_array($param1) ?: is_array($param2) ?: is_array($param3); return (object) [ 'url' => $url, 'method' => $method, 'params' => $params ]; } }所以根据不同的参数顺序和参数类型,您可以选择一种简洁清晰和习惯的方式进行请求发送。send 方法力求代码的灵活性,同时也不会造成使用上的困扰。 助手函数 轻HTTP请求库提供以下助手函数: post() 发送POST请求。用法: network\http\post($url, $params, $headers, $options) $url: 请求URL $params: 携带的参数,可以是数组或false $headers: 自定义请求头,数组格式 $options: 配置信息,数组格式 返回response对象。 示例: $response = network\http\post('http://www.bri6.cn', ['name' => '易航']);get() 发送GET请求。用法: network\http\get($url, $params, $headers, $options)参数与post()方法相同。 示例: $response = network\http\get('http://www.bri6.cn', ['category' => 'technology']);delete() 发送DELETE请求。用法: network\http\delete($url, $params, $headers, $options)参数与post()方法相同。 示例: $response = network\http\delete('http://www.bri6.cn'); put() 发送PUT请求。用法: network\http\put($url, $params, $headers, $options)参数与post()方法相同。 示例: $response = network\http\put('http://www.bri6.cn', ['title' => 'Easy']);patch() 发送PATCH请求。用法: network\http\patch($url, $params, $headers, $options)参数与post()方法相同。 示例: $response = network\http\patch('http://www.bri6.cn', ['views' => 999]); 获取响应信息 使用响应对象获取响应信息: $response = $client->get('http://bri6.cn');可以从响应对象中获取以下信息: $response->code() :获取响应状态码 示例: $code = $response->code(); // 200 $response->header('name') : 获取指定响应头 示例: $content_type = $response->header('Content-Type'); // application/json $response->headers(): 以数组形式获取所有响应头 示例: $headers = $response->headers(); /* [ 'Content-Type' => 'application/json', 'X-Powered-By' => 'PHP/7.2.10' ] */ $response->body(): 获取原始响应体字符串 示例: $body = $response->body(); // '{"name": "易航"}' $response->toObject(): 如果响应是JSON,转换为对象 示例: $data = $response->toObject(); // stdClass Object // ( // [name] => 易航 // ) $response->toArray(): 如果响应是JSON,转换为数组 示例: $data = $response->toArray(); // ['name' => '易航']显示响应体 可以直接输出响应体: echo $response;或者: echo $response->body();这将直接输出响应的原始字符串内容。 示例: $response = $client->get('http://www.bri6.cn'); echo $response; // {"id": 25, "title": "Easy PHP", "content": "Some content here", "views": 999} echo $response->body(); // {"id": 25, "title": "Easy PHP", "content": "Some content here", "views": 999}当您直接输出 $response 对象时,PHP会自动调用 __toString() 魔术方法,并返回响应体字符串。 如果响应的内容类型是JSON,调用 toObject() 或 toArray() 方法转换后再输出也是很好的选择: $data = $response->toArray(); print_r($data); /* [ "id" => 25, "title" => "Easy PHP", "content" => "Some content here", "views" => 999 ] */ 错误与异常 请求过程中发生的curl错误或解析响应时发生的错误将抛出异常,使用try/catch进行捕获。 示例: try { $response = $client->get('http://www.bri6.cn'); } catch (Exception $e) { echo $e->getMessage(); // CURLOPT_URL: Failure (2) 'The requested URL returned error: 404 NOT FOUND' }轻HTTP请求库使用了GuzzleHttp作为底层HTTP客户端,所以会抛出GuzzleHttp相关的异常,常见的有: ConnectionException: 连接异常 RequestException: 请求异常 ClientException: 客户端错误(4xx响应) BadResponseException: 服务器错误(5xx响应) TooManyRedirectsException: 重定向过多异常 所有的异常都继承自Exception,可以使用try/catch捕获。 当解析响应JSON数据时发生错误,也会抛出异常: try { $data = $response->toObject(); } catch (InvalidJsonException $e) { echo $e->getMessage(); // Syntax error, malformed JSON }InvalidJsonException异常是由轻HTTP请求库自己抛出的,用于表明响应JSON数据格式错误。 错误和异常处理是开发一个健全的应用很重要的一环,希望这个错误与异常页面能帮助您更好地处理轻HTTP请求库可能产生的各种异常情况。如果您有任何疑问,请告知我。 其他 另外,如果需要对请求库进行定制开发,可以继承Client类并重写send()方法: namespace network\http; class CustomClient extends Client { public function send($url, $params, $headers) { // 定制发送请求的逻辑 // 调用parent::send($url, $params, $headers)发送请求 } }然后通过 new CustomClient() 使用定制的客户端。 希望这个HTTP客户端库和使用文档能为您提供帮助!如果有任何问题请提Issue或Pull Request。 我会持续更新文档,完整记录轻HTTP请求库的所有功能和用法。如果文档的任何部分不够详尽,请提Issue告知我。 希望这个简洁实用的轻HTTP请求库和配套文档能为广大PHP开发者提供更多便捷!
技术教程
# PHP
易航
11月2日
0
101
0
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
易航
9月23日
0
66
0
1
2
...
4
下一页
易航博客