首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
136 阅读
2
php接口优化 使用curl_multi_init批量请求
130 阅读
3
2024年备考系统架构设计师
102 阅读
4
《从菜鸟到大师之路 ElasticSearch 篇》
101 阅读
5
PHP 文件I/O
89 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
785
篇文章
累计收到
5
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
559
篇与
的结果
2023-12-23
MySQL隐式转换还有这问题?
MySQL隐式转换还有这问题?结论当操作符 左右两边的数据类型 不一致时,会发生隐式转换。当 where 查询操作符 左边为数值类型 时发生了隐式转换,那么对效率影响不大,但还是不推荐这么做。当 where 查询操作符 左边为字符类型 时发生了隐式转换,那么会导致索引失效,造成全表扫描效率极低。字符串转换为数值类型时,非数字开头的字符串会转化为0,以数字开头的字符串会截取从第一个字符到第一个非数字内容为止的值为转化结果。所以,最好的解决方案就是我们一定要养成良好的SQL习惯,where条件的列是什么类型我们就应该传递什么类型,尽量不让MySQL来做隐式转换,就如同上述的情况一,由于隐式转换导致了MySQL不会走索引,导致索引失效触发全表扫描的问题。串会转化为0,以数字开头的字符串会截取从第一个字符到第一个非数字内容为止的值为转化结果。所以,最好的解决方案就是我们一定要养成良好的SQL习惯,where条件的列是什么类型我们就应该传递什么类型,尽量不让MySQL来做隐式转换,就如同上述的情况一,由于隐式转换导致了MySQL不会走索引,导致索引失效触发全表扫描的问题。mysql> desc student; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | name | varchar(50) | YES | | NULL | | | code | int | YES | | NULL | | | sex | tinyint(1) | NO | | 1 | | +-------+-------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) mysql> insert into student value(0,3,110001,1); Query OK, 1 row affected (0.00 sec) mysql> select * from student; +----+-----------+------------+-----+ | id | name | code | sex | +----+-----------+------------+-----+ | 1 | 张三丰 | 20181601 | 1 | | 2 | 尔四 | 20181602 | 1 | | 3 | 小红 | 20181603 | 1 | | 4 | 小明 | 20181604 | 1 | | 5 | 小青 | 20181605 | 1 | | 6 | 小明 | 20191302 | 1 | | 7 | 22 | 2138291723 | 1 | | 8 | 李明 | 654368 | 2 | | 10 | 3 | 110001 | 1 | +----+-----------+------------+-----+ 9 rows in set (0.00 sec) mysql> select * from student where name="3"; +----+------+--------+-----+ | id | name | code | sex | +----+------+--------+-----+ | 10 | 3 | 110001 | 1 | +----+------+--------+-----+ 1 row in set (0.00 sec) mysql> select * from student where name=3; +----+------+--------+-----+ | id | name | code | sex | +----+------+--------+-----+ | 10 | 3 | 110001 | 1 | +----+------+--------+-----+ 1 row in set, 7 warnings (0.00 sec)
2023年12月23日
13 阅读
0 评论
0 点赞
2023-12-22
关于PHP不得不说的那几件事
PHP老矣,尚能饭否?(51CTO)语言类型是解释型还是编译型?PHP是一种通用的服务器端脚本语言,经常用于开发动态网页和Web应用程序。它最初设计用于处理网页上的表单数据,但随着时间的推移,它逐渐发展成为一个功能强大的编程语言。PHP被归类为解释型语言。这意味着PHP代码在运行之前不需要显式编译成机器码。相反,PHP脚本以文本形式在服务器上执行,并由PHP解释器逐行解释执行。每当服务器收到对包含PHP代码的网页的请求时,它都会动态地解析和执行代码。解释型语言的一个优点是它的开发过程较为灵活。您可以直接修改PHP代码并立即查看结果,无需额外的编译步骤。这也使得PHP在Web开发中非常受欢迎,因为您可以快速进行迭代和调试。需要注意的是,PHP在执行代码时会进行一些优化,例如将解释的代码缓存起来以提高执行速度。这种缓存机制可以显著降低每次请求的解释和执行开销。总之,PHP是一种解释型语言,它在服务器上动态解析和执行代码,为开发者提供便捷的开发和调试过程。还有哪些解释型语言,哪些是编译型语言以及它们的优点缺点解释型语言和编译型语言是两种常见的编程语言类型。下面是一些常见的解释型语言和编译型语言,以及它们的特点、优点和缺点的详细说明:解释型语言(动态语言):特点:解释型语言的代码在运行时逐行解释执行,而无需显式的编译过程。它们通常具有动态类型系统,在运行时可以更灵活地处理数据类型。例子:Python、Ruby、PHP、JavaScript等。优点:快速开发:解释型语言通常具有简洁的语法和内置的高级功能,可以快速编写代码并迅速迭代开发。跨平台性:由于解释器本身可以在各种操作系统上运行,解释型语言通常具有很好的跨平台兼容性。灵活性:动态类型系统允许在运行时更灵活地处理类型,简化了代码编写和修改的过程。缺点:运行速度:解释型语言的代码在运行时需要逐行解释执行,相对于编译型语言来说,其运行速度可能较慢。可移植性:由于依赖于解释器,解释型语言的可移植性可能较差,需要确保在目标环境中有适当的解释器可用。编译型语言(静态语言):特点:编译型语言的代码在运行之前需要经过显式的编译过程,将源代码编译成可执行的机器码。它们通常具有静态类型系统,在编译时对类型进行检查。例子:C、C++、Java、Go等。优点:高执行效率:编译型语言的代码在运行时已经编译成机器码,因此其执行速度通常更快。严格的类型检查:静态类型系统可以在编译时检查类型错误,提前发现潜在的问题,使得代码更加健壮和可靠。较好的可维护性:编译型语言通常具有较强的模块化和面向对象的特性,使得代码易于组织和维护。缺点:开发速度:编译型语言通常在编写代码和进行调试时需要更多的时间和工作量。学习曲线:相对于解释型语言,编译型语言可能具有更复杂的语法和更严格的规则,需要更多的学习和掌握。需要注意的是,解释型语言和编译型语言之间并没有绝对的界限,有些语言也可以结合两者的特性。例如,Java是一种编译型语言,但它也使用了即时编译器(Just-In-Time, JIT)来优化运行性能。总结来说,解释型语言通常适用于快速开发、动态环境和跨平台需求,而编译型语言则适用于高性能、严格类型检查和可维护性的需求。选择合适的语言取决于具体应用场景和开发需求。php各个版本特性下面是一些PHP版本中的主要特性,以及每个特性的详细说明和示例:1. PHP 5.3 版本特性:命名空间(Namespaces):允许在代码中创建独立的命名空间,避免命名冲突,并提高代码的可组织性和可维护性。<?php namespace MyNamespace; class MyClass { // 类定义 } function myFunction() { // 函数定义 } ?>闭包(Closures):引入匿名函数的概念,可以在运行时创建函数并将其作为值传递、分配给变量、存储在数据结构中或作为回调函数使用。<?php $greeting = function($name) { echo "Hello, $name!"; }; $greeting("John"); // 输出:Hello, John! ?>短数组语法(Short Array Syntax):允许使用方括号快速创建数组。<?php $numbers = [1, 2, 3, 4, 5]; $person = ["name" => "John", "age" => 30]; ?>2. PHP 5.4 版本特性:Traits:引入了Traits的概念,允许多个类之间共享方法的代码块,提供了一种代码复用的机制。<?php trait Loggable { public function log($message) { echo "{$message}\n"; } } class User { use Loggable; // User类的其他代码 } $user = new User(); $user->log("User created."); // 输出:User created. ?>短数组语法中允许使用字符串键名(String Key Array):<?php $person = ["name" => "John", "age" => 30]; echo $person["name"]; // 输出:John ?>内置HTTP服务器(Built-in HTTP Server):可以通过命令行启动PHP内置的轻量级HTTP服务器,方便开发和测试。$ php -S localhost:80003. PHP 5.5 版本特性:生成器(Generators):引入生成器函数,可以逐步生成值,而不是一次性生成所有值,在处理大型数据集时更加高效。<?php function numberGenerator() { for ($i = 0; $i < 10; $i++) { yield $i; } } foreach (numberGenerator() as $number) { echo "$number "; } // 输出:0 1 2 3 4 5 6 7 8 9 ?>4. PHP 5.6 版本特性:可变数量的函数参数(Variadic Function Arguments):允许函数接受不定数量的参数,简化了函数的定义和使用。<?php function sum(...$numbers) { $total = 0; foreach ($numbers as $number) { $total += $number; } return $total; } echo sum(1, 2, 3, 4, 5); // 输出:15 ?>可变数量的参数传递给call_user_func_array()函数:<?php function greetings($name, $age) { echo "Hello, $name! You are $age years old."; } $args = ["John", 30]; call_user_func_array("greetings", $args); // 输出:Hello, John! You are 30 years old. ?>5. PHP 7 版本特性:标量类型声明(Scalar Type Declarations):可以在函数参数和返回类型中声明标量类型(整数、浮点数、布尔值和字符串),提高代码的可靠性和可读性。<?php function multiply(int $a, float $b): float { return $a * $b; } echo multiply(5, 2.5); // 输出:12.5 ?>返回类型声明(Return Type Declarations):可以在函数定义中指定返回值的类型。<?php function getFullName(): string { $firstName = "John"; $lastName = "Doe"; return "$firstName $lastName"; } echo getFullName(); // 输出:John Doe ?>空合并运算符(Null Coalescing Operator):简化了检查和使用可能为null的变量的代码。<?php $username = $_GET["username"] ?? "Guest"; echo "Welcome, $username!"; // 如果$_GET["username"]不存在,输出:Welcome, Guest! ?>6. PHP 8 版本特性:PHP 8是PHP语言的最新版本,带来了一系列强大的特性和改进。以下是PHP 8的一些主要特性和相应的详细说明和示例:JIT(Just-In-Time)编译器:PHP 8引入了JIT编译器,它可以在运行时将PHP代码直接编译成本机机器代码,提高执行性能。JIT编译器可以通过在php.ini文件中进行配置来启用。Union 类型:PHP 8允许在类型声明中指定联合类型,即变量可以是多个类型之一。这提供了更大的灵活性和可读性,比如一个变量可以是整数或浮点数类型。<?php function processNumber(int|float $number) { // 对整数和浮点数执行处理逻辑 } processNumber(10); // 正确 processNumber(3.14); // 正确 processNumber("abc"); // 错误,不是允许的类型 ?>命名参数:PHP 8引入了命名参数,允许在函数调用时通过名称指定参数值,而不是按照参数顺序传递。这样可以提高函数调用的可读性和可维护性。<?php function greet($name, $age) { echo "Hello, $name! You are $age years old."; } greet(age: 30, name: "John"); // 输出:Hello, John! You are 30 years old. ?>Match 表达式:PHP 8中引入了Match表达式,它提供了在多个条件中进行严格值匹配的一种替代方式。<?php $value = 2; $result = match($value) { 1 => "One", 2 => "Two", 3 => "Three", default => "Other" }; echo $result; // 输出:Two ?>nullsafe 运算符:PHP 8中添加了nullsafe运算符(?->),它允许在链式调用中对可能为null的对象进行安全访问,避免了繁琐的条件检查。<?php class User { public function getName() { return "John"; } } class Order { public function getUser() { return null; // 设置为null以模拟对象不存在 } } $order = new Order(); $name = $order->getUser()?->getName(); echo $name; // 输出:null,而不会引发错误 ?>松散类型检查:在PHP 8中,松散类型检查被弃用,使得严格类型检查成为默认行为。这提高了代码的可靠性和一致性,但也需要开发者更加注意类型的正确使用。<?php declare(strict_types=1); // 启用严格类型检查 function sum(int $a, int $b): int { return $a + $b; } echo sum(5, 2); // 输出:7 echo sum(5.5, 2.5); // 错误,参数类型不匹配 ?>上述只是一些PHP版本中引入的主要特性的例子,每个版本还可能包含其他特性和改进。如果您需要更详细的特性列表和使用指南,建议查阅PHP官方文档。官方文档提供了每个版本的详细特性介绍,包括语法示例、用法说明以及与之前版本的兼容性考虑。它是学习和了解各个PHP版本特性的重要资源。
2023年12月22日
15 阅读
0 评论
0 点赞
2023-12-18
phpy :PHP 与 Python 互调用库,为 PHP 引入 Python 生态,PHP 也可以写 AI 了
phpy :PHP 与 Python 互调用库,为 PHP 引入 Python 生态,PHP 也可以写 AI 了phpy 是识沃团队最新推出的开源项目,目标是为 PHP 引入 Python 生态,来弥补 PHP 生态的空缺和不足。phpy 使得 PHP 可以调用所有 Python 的包。包括当下非常流行的 PyTorch、transformers、TensorFlow 等 AI 库,以及 Numpy、Pandas、Scikit 等科学计算库,还可以使用 PyQt、wxPython 等图形界面库。GitHub 地址:https://github.com/swoole/phpy不建议在 php-fpm/apache 短生命周期运行环境下使用,频繁地导入/销毁模块的开销会消耗大量资源编译安装phpy 可以作为 PHP 的扩展,也可以作为 Python 的 C 模块。既可以在 PHP 代码中调用 Python 的库,也可以在 Python 中调用 PHP 的类和函数。作为 Python 模块时依赖 PHP 的 embed SAPI ,检查 PHP 的目录中,确保存在 libphp.soll /opt/php-8.1/lib/libphp.so -rwxr-xr-x 1 htf htf 39397224 11月 30 19:25 /opt/php-8.1/lib/libphp.so*编译依赖Python 3.10 或以上版本,建议使用 conda 工具来安装PHP 8.1 或以上版本Python 将安装到 /opt/anaconda3 目录下/opt/anaconda3/bin/python`Python` 主程序/opt/anaconda3/include/python3.11 头文件/opt/anaconda3/lib/python3.11 动态链接库目录另外需要配置 /etc/ld.so.conf.d/conda.conf 加入 /opt/anaconda3/lib 和 /opt/php-8.1/lib 。执行 ldconfig 检查是否可以找到 libpython3.11.so 和 libphp.so。sudo ldconfig -p |grep php libphp7.so (libc6,x86-64) => /opt/php-7.4/lib/libphp7.so libphp.so (libc6,x86-64) => /opt/php-8.0/lib/libphp.so sudo ldconfig -p |grep python libsamba-policy.cpython-38-x86-64-linux-gnu.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libsamba-policy.cpython-38-x86-64-linux-gnu.so.0 libpython3.11.so.1.0 (libc6,x86-64) => /opt/anaconda3/lib/libpython3.11.so.1.0 libpython3.11.so (libc6,x86-64) => /opt/anaconda3/lib/libpython3.11.so libpython3.8.so.1.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0 libpython3.8.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython3.8.so libpython3.5m.so.1.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0 libpython3.so (libc6,x86-64) => /opt/anaconda3/lib/libpython3.so libpython2.7.so.1.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0 libpython2.7.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython2.7.so作为 PHP 扩展检查 config.m4 中 Python 路径是否正确。若 Python 的安装路径不是 /opt/anaconda3,需修改为正确的安装路径。cd phpy phpize ./configure make install安装成功后,修改 php.ini ,加入 extension=phpy.so,执行 php -m 和 php --ri phpy 检查是否成功加载扩展。作为 Python 模块cmake . make -j执行成功后,会生成 tests/lib/phpy.so 文件。可以在 Python 中直接导入此模块。import phpy使用方法导入 Python 模块$os = PyCore::import('os');执行函数$uname = $os->uname();读取属性echo $uname->sysname;加载路径可使用 PyCore::import('sys')->path->append() 将一些目录加入到加载路径列表中。 例如:/workspace/app/user.py 自定义的包,可以通过下面的步骤实现加载:PyCore::import('sys')->path->append('/workspace') 将 /workspace 添加到 sys.path 中PyCore::import('app.user') 将自动搜索 sys.path 找到对应的 app/user.py 包并载入内置方法PyCore::str() 将对象转为字符串PyCore::repr()PyCore::type() 获取对象的类型PyCore::locals() 获取当前空间内容的所有局部变量PyCore::globals() 获取所有全局变量PyCore::hash() 获取 Hash 值PyCore::hasattr() 检测对象是否存在某个属性PyCore::id() 获取对象的内部编号PyCore::len() 获取长度PyCore::dir() 获取对象所有的属性、方法PyCore::int() 构造一个整数PyCore::float() 构造一个浮点数PyCore::fn() 构造一个可调用函数PyCore::scalar() 将 PyObject 对象转为 PHP 的标量类型,例如 PyStr 将转为 PHP 字符串,Dict/Tuple/Set/List 将转为 Array内置类PyObject:所有其他类型的基类PyDict:字典类型,等同于 PHP 的关联数组PyList:列表类型,等同于 PHP 的索引数组PyTuple:元组,不可变的列表PyStr:字符串PyModule:Python 包,PyModule 也是 PyObject 的子类PyObject 是除了 PyCore 之外,所有其他类型的基类。非内置类的对象是 PyObject 的实例。PyObject 实现了 4 个魔术方法,用于将操作映射到 Python 对象。所有类方法、参数、返回值参考 stubs 目录中的文件。继承关系PyObject -> PyModule -> PySequenece -> PyList -> PyTuple -> PySet -> PyStr -> PyDict -> PyType整数Python 语言是天然支持无限精度整型计算的,可以使用 Python 的整数计算能力来代替 ext-bcmath构造使用 PyCore::int() 函数来构造一个数字,可以传入整数、浮点数、字符串来初始化。$i1 = PyCore::int(12345678);$i2 = PyCore::int('1234567890123456789012345678901234567890');$i3 = PyCore::int(12345678.03);运算整数同样也是 PyObject 的实例,可以使用内置的方法类实现运算。$i = PyCore::int(12345435);var_dump(strval($i->__pow__(3)));var_dump(strval($i->__add__(4)));将输出 1881564851360655187875 ,由于超过了 64位 最大精度,因此输出结果将自动转为字符串类型。命名参数phpy 支持了命名参数,可以使用命名参数来调用 Python 的函数和方法。顺序参数必须在前,命名参数必须在最后kwargs($a, $b, $c, name: 'hello', world: 'rango');对应的 Python 代码为:kwargs(a, b, c, name: 'hello', world: 'rango')回调函数可将 PHP 的可调用对象作为 Python 的回调函数。使用 PyCore::fn(callable $fn) 包裹即可。$m = PyCore::import('app.user');$uuid = uniqid();$rs = $m->test_callback(PyCore::fn(function ($namespace) use ($uuid) { var_dump($namespace); return $uuid;}));import app.user 导入了一个自定义 Python 包调用了包中的一个函数 test_callback,此函数接受一个参数为 Python Callable 对象使用 PyCore::fn() 包裹了一个 Closure 闭包对象作为回调,这里也支持函数名称字符串、对象方法的调用方式回调函数返回了一个字符串,在 test_callback 函数中会得到一个 str 类型返回值可参考下方的 Python tkinter 例子。实际案例基于 tkinter 实现 GUI 的例子<?php$tkinter = PyCore::import('tkinter');$root = $tkinter->Tk();$root->title('我的窗口');$root->geometry("500x500");$root->resizable(False, False);$button = $tkinter->Button($root, text: "Click Me!!", command: PyCore::fn(function () { var_dump(func_get_args()); echo 'click me!!' . PHP_EOL;}));$button->pack();$tkinter->mainloop();一个基于 transformers 的情感分析模型推理实现<?php$transformers = PyCore::import('transformers');$os = PyCore::import('os');$os->environ->__setitem__('https_proxy', getenv('https_proxy'));$distilled_student_sentiment_classifier = $transformers->pipeline( model: "lxyuan/distilbert-base-multilingual-cased-sentiments-student", top_k: null,);$rs = $distilled_student_sentiment_classifier ("I love this movie and i would watch it again and again!");var_dump(PyCore::scalar($rs));
2023年12月18日
12 阅读
0 评论
0 点赞
2023-12-13
什么是面向切片编程?
面向切片(Aspect-oriented Programming,AOP)是一种软件开发的方法论,它旨在通过将横切关注点从主要业务逻辑中分离出来,提供更高层次的模块化和可维护性。它通过将跨越多个对象、类或组件的共同功能(称为横切关注点)从核心业务逻辑中提取出来,并以模块化方式将其封装起来。面向切片编程可以用于解决以下几种常见问题:日志记录:通过在关键方法的前后添加日志记录的代码,可以实现统一的日志记录功能,而无需修改每个方法。事务管理:通过将事务管理代码从业务逻辑中分离出来,可以实现更加灵活和可重用的事务处理机制。安全性检查:通过在关键方法的前后添加安全性检查的代码,可以确保只有具有特定权限的用户才能访问敏感信息。性能监控:通过在关键方法的前后添加性能监控代码,可以实现对系统性能的实时监测和统计。下面以日志记录为例进行详细说明。假设我们有一个银行账户管理系统,其中涉及到以下两个核心方法:public void deposit(double amount) { // 存款操作 } public void withdraw(double amount) { // 取款操作 }为了实现日志记录功能,我们可以创建一个切面(Aspect),并在切面中定义一个通知(Advice)。通知是在特定的连接点(Join Point)上执行的代码片段,例如方法调用或方法执行的前后。public aspect LoggingAspect { before(): execution(public void deposit(double)) { System.out.println("记录存款操作日志"); } before(): execution(public void withdraw(double)) { System.out.println("记录取款操作日志"); } }上述代码中,LoggingAspect 是切面类,before() 是前置通知。它使用 execution(public void deposit(double)) 来指定连接点为 deposit() 方法的执行前,同样地, execution(public void withdraw(double)) 指定连接点为 withdraw() 方法的执行前。当程序运行时,切面中的通知代码会在指定的连接点之前执行,从而实现了在关键方法的执行前添加日志记录的功能。总结来说,面向切片编程通过将横切关注点从主要业务逻辑中分离出来,提供了一种方法将共性功能模块化,并在需要时以声明式方式将其应用到不同的连接点上。这种方式能够提高代码的可维护性、可重用性和灵活性,使得系统更易于扩展和修改。
2023年12月13日
11 阅读
0 评论
0 点赞
2023-12-11
如何编写编写干净的 PHP 代码
如何编写编写干净的 PHP 代码介绍软件工程原理,摘自 Robert C. Martin 的著作 Clean Code, 适用于PHP。这不是风格指南。这是一份生产指南 PHP 中可读、可重用和可重构的软件。并非这里的每一项原则都必须严格遵守,甚至更少是普遍遵守的 商定。这些是指导方针,仅此而已,但它们是许多准则的编纂 _Clean Code_ 作者多年的集体经验。灵感来自 clean-code-javascript。尽管许多开发人员仍在使用 PHP 5,但本文中的大多数示例仅适用于 PHP 7.1+。变量使用有意义且可发音的变量名称坏:$ymdstr = $moment->format('y-m-d');好:$currentDate = $moment->format('y-m-d');对相同类型的变量使用相同的词汇坏:getUserInfo(); getUserData(); getUserRecord(); getUserProfile();好:getUser();使用可搜索的名称(第 1 部分)我们将阅读比我们编写的更多的代码。重要的是,我们编写的代码是 可读和可搜索。通过_不_命名最终有意义的变量 了解我们的程序,我们伤害了我们的读者。 使你的名字可搜索。坏:// What the heck is 448 for? $result = $serializer->serialize($data, 448);好:$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);使用可搜索的名称(第 2 部分)坏:class User { // What the heck is 7 for? public $access = 7; } // What the heck is 4 for? if ($user->access & 4) { // ... } // What's going on here? $user->access ^= 2;好:class User { public const ACCESS_READ = 1; public const ACCESS_CREATE = 2; public const ACCESS_UPDATE = 4; public const ACCESS_DELETE = 8; // User as default can read, create and update something public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE; } if ($user->access & User::ACCESS_UPDATE) { // do edit ... } // Deny access rights to create something $user->access ^= User::ACCESS_CREATE;使用解释变量坏:$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]);不错:它更好,但我们仍然严重依赖正则表达式。$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); [, $city, $zipCode] = $matches; saveCityZipCode($city, $zipCode);好:通过命名子模式来减少对正则表达式的依赖。$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches['city'], $matches['zipCode']);避免嵌套太深,提早返回(第 1 部分)过多的 if-else 语句会使代码难以理解。显式更好 比隐含。坏:function isShopOpen($day): bool { if ($day) { if (is_string($day)) { $day = strtolower($day); if ($day === 'friday') { return true; } elseif ($day === 'saturday') { return true; } elseif ($day === 'sunday') { return true; } return false; } return false; } return false; }好:function isShopOpen(string $day): bool { if (empty($day)) { return false; } $openingDays = ['friday', 'saturday', 'sunday']; return in_array(strtolower($day), $openingDays, true); }避免嵌套太深,提早返回(第 2 部分)坏:function fibonacci(int $n) { if ($n < 50) { if ($n !== 0) { if ($n !== 1) { return fibonacci($n - 1) + fibonacci($n - 2); } return 1; } return 0; } return 'Not supported'; }好:function fibonacci(int $n): int { if ($n === 0 || $n === 1) { return $n; } if ($n >= 50) { throw new Exception('Not supported'); } return fibonacci($n - 1) + fibonacci($n - 2); }避免思维导图不要强迫代码的读者翻译变量的含义。 显式比隐式好。坏:$l = ['Austin', 'New York', 'San Francisco']; for ($i = 0; $i < count($l); $i++) { $li = $l[$i]; doStuff(); doSomeOtherStuff(); // ... // ... // ... // Wait, what is `$li` for again? dispatch($li); }好:$locations = ['Austin', 'New York', 'San Francisco']; foreach ($locations as $location) { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch($location); }不要添加不需要的上下文如果你的类/对象名称告诉你一些事情,不要在你的 变量名称。坏:class Car { public $carMake; public $carModel; public $carColor; //... }好:class Car { public $make; public $model; public $color; //... }比较使用相同的比较不好:简单的比较会将字符串转换为整数。$a = '42'; $b = 42; if ($a != $b) { // The expression will always pass }比较回来了,但实际上是! 字符串与整数不同。$a != $b`FALSETRUE42`42好:相同的比较将比较类型和值。$a = '42'; $b = 42; if ($a !== $b) { // The expression is verified }比较返回 。$a !== $b`TRUE`Null 合并运算符Null 合并是 PHP 7 中引入的一个新运算符。已将 null 合并运算符添加为句法糖,用于需要将三元与 结合使用的常见情况。如果它存在并且不存在,则返回其第一个操作数;否则,它将返回其第二个操作数。??`isset()`null坏:if (isset($_GET['name'])) { $name = $_GET['name']; } elseif (isset($_POST['name'])) { $name = $_POST['name']; } else { $name = 'nobody'; }好:$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody';功能使用默认参数而不是短路或条件不好:这不好,因为可以.$breweryName`NULL`function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void { // ... }不错:这种观点比以前的版本更容易理解,但它更好地控制了变量的值。function createMicrobrewery($name = null): void { $breweryName = $name ?: 'Hipster Brew Co.'; // ... }好:您可以使用类型提示,并确保 不会是 .$breweryName`NULL`function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void { // ... }函数参数(理想情况下为 2 个或更少)限制函数参数的数量非常重要,因为它使 更轻松地测试您的函数。拥有三个以上会导致组合爆炸 你必须用每个单独的参数测试大量不同的情况。零参数是理想的情况。一两个论点是可以的,应该避免三个论点。 除此之外,任何事情都应该被合并。通常,如果您有两个以上的 参数,那么你的函数试图做太多。如果不是,大多数 在更高级别的对象作为参数就足够了。坏:class Questionnaire { public function __construct( string $firstname, string $lastname, string $patronymic, string $region, string $district, string $city, string $phone, string $email ) { // ... } }好:class Name { private $firstname; private $lastname; private $patronymic; public function __construct(string $firstname, string $lastname, string $patronymic) { $this->firstname = $firstname; $this->lastname = $lastname; $this->patronymic = $patronymic; } // getters ... } class City { private $region; private $district; private $city; public function __construct(string $region, string $district, string $city) { $this->region = $region; $this->district = $district; $this->city = $city; } // getters ... } class Contact { private $phone; private $email; public function __construct(string $phone, string $email) { $this->phone = $phone; $this->email = $email; } // getters ... } class Questionnaire { public function __construct(Name $name, City $city, Contact $contact) { // ... } }函数名称应说明它们的作用坏:class Email { //... public function handle(): void { mail($this->to, $this->subject, $this->body); } } $message = new Email(...); // What is this? A handle for the message? Are we writing to a file now? $message->handle();好:class Email { //... public function send(): void { mail($this->to, $this->subject, $this->body); } } $message = new Email(...); // Clear and obvious $message->send();函数应该只是一个抽象级别当你有多个抽象级别时,你的函数通常是 做得太多了。拆分功能可提高可重用性,更轻松 测试。坏:function parseBetterPHPAlternative(string $code): void { $regexes = [ // ... ]; $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { // ... } } $ast = []; foreach ($tokens as $token) { // lex... } foreach ($ast as $node) { // parse... } }也不好:我们已经执行了一些功能,但功能仍然非常复杂且无法测试。parseBetterPHPAlternative()function tokenize(string $code): array { $regexes = [ // ... ]; $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { $tokens[] = /* ... */; } } return $tokens; } function lexer(array $tokens): array { $ast = []; foreach ($tokens as $token) { $ast[] = /* ... */; } return $ast; } function parseBetterPHPAlternative(string $code): void { $tokens = tokenize($code); $ast = lexer($tokens); foreach ($ast as $node) { // parse... } }好:最好的解决方案是移出函数的依赖关系。parseBetterPHPAlternative()class Tokenizer { public function tokenize(string $code): array { $regexes = [ // ... ]; $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { $tokens[] = /* ... */; } } return $tokens; } } class Lexer { public function lexify(array $tokens): array { $ast = []; foreach ($tokens as $token) { $ast[] = /* ... */; } return $ast; } } class BetterPHPAlternative { private $tokenizer; private $lexer; public function __construct(Tokenizer $tokenizer, Lexer $lexer) { $this->tokenizer = $tokenizer; $this->lexer = $lexer; } public function parse(string $code): void { $tokens = $this->tokenizer->tokenize($code); $ast = $this->lexer->lexify($tokens); foreach ($ast as $node) { // parse... } } }不要使用标志作为函数参数标志告诉用户此函数执行多项操作。函数应 做一件事。如果函数遵循不同的代码路径,请拆分函数 基于布尔值。坏:function createFile(string $name, bool $temp = false): void { if ($temp) { touch('./temp/' . $name); } else { touch($name); } }好:function createFile(string $name): void { touch($name); } function createTempFile(string $name): void { touch('./temp/' . $name); }避免副作用如果函数执行除 和 中的值之外的任何操作,则会产生副作用 返回另一个或多个值。副作用可能是写入文件、修改 一些全局变量,或者不小心把你所有的钱都汇给一个陌生人。现在,您确实需要偶尔在程序中出现副作用。和以前一样 例如,您可能需要写入文件。您要做的是集中在哪里 你正在这样做。不要有多个函数和类写入特定的 文件。有一个服务可以做到这一点。一个也是唯一一个。重点是避免常见的陷阱,例如在没有 任何结构,使用可变数据类型,可以由任何内容写入,而不是 集中出现副作用的位置。如果你能做到这一点,你会更快乐 比绝大多数其他程序员都要好。坏:// Global variable referenced by following function. // If we had another function that used this name, now it'd be an array and it could break it. $name = 'Ryan McDermott'; function splitIntoFirstAndLastName(): void { global $name; $name = explode(' ', $name); } splitIntoFirstAndLastName(); var_dump($name); // ['Ryan', 'McDermott'];好:function splitIntoFirstAndLastName(string $name): array { return explode(' ', $name); } $name = 'Ryan McDermott'; $newName = splitIntoFirstAndLastName($name); var_dump($name); // 'Ryan McDermott'; var_dump($newName); // ['Ryan', 'McDermott'];不要写入全局函数在许多语言中,污染全局变量是一种不好的做法,因为您可能会与另一种语言发生冲突 库和 API 的用户将不明智,直到他们在 生产。让我们考虑一个例子:如果你想拥有配置数组怎么办? 你可以像 一样编写全局函数,但它可能会与另一个库发生冲突 试图做同样的事情。config()坏:function config(): array { return [ 'foo' => 'bar', ]; }好:class Configuration { private $configuration = []; public function __construct(array $configuration) { $this->configuration = $configuration; } public function get(string $key): ?string { // null coalescing operator return $this->configuration[$key] ?? null; } }加载配置并创建类的实例Configuration$configuration = new Configuration([ 'foo' => 'bar', ]);现在,您必须在应用程序中使用实例。Configuration不要使用单一实例模式Singleton 是一种反模式。转述自布莱恩·巴顿(Brian Button):它们通常被用作全局实例,为什么这么糟糕?因为您将应用程序的依赖项隐藏在代码中,而不是通过接口公开它们。制作全球性的东西以避免传递它是一种代码味道。他们违反了单一责任原则:因为他们控制着自己的创造和生命周期。它们固有地导致代码紧密耦合。这使得在许多情况下在测试中伪造它们变得相当困难。它们在应用程序的生存期内携带状态。测试的另一个打击,因为你最终可能会遇到需要订购测试的情况,这对单元测试来说是一个很大的否定。为什么?因为每个单元测试都应该彼此独立。Misko Hevery对问题的根源也有很好的想法。坏:class DBConnection { private static $instance; private function __construct(string $dsn) { // ... } public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } // ... } $singleton = DBConnection::getInstance();好:class DBConnection { public function __construct(string $dsn) { // ... } // ... }创建类的实例并使用 DSN 对其进行配置。DBConnection$connection = new DBConnection($dsn);现在,您必须在应用程序中使用实例。DBConnection封装条件语句坏:if ($article->state === 'published') { // ... }好:if ($article->isPublished()) { // ... }避免负面条件坏:function isDOMNodeNotPresent(DOMNode $node): bool { // ... } if (! isDOMNodeNotPresent($node)) { // ... }好:function isDOMNodePresent(DOMNode $node): bool { // ... } if (isDOMNodePresent($node)) { // ... }避免使用条件这似乎是一项不可能完成的任务。第一次听到这句话时,大多数人会说, “没有声明,我怎么能做任何事情?”答案是 在许多情况下,您可以使用多态性来实现相同的任务。第二个 问题通常是,“嗯,这很好,但我为什么要这样做?这 答案是我们之前学到的一个干净的代码概念:一个函数应该只做 一件事。当您具有具有语句的类和函数时,您 告诉用户您的函数执行了不止一件事。记得 只做一件事。if`if`坏:class Airplane { // ... public function getCruisingAltitude(): int { switch ($this->type) { case '777': return $this->getMaxAltitude() - $this->getPassengerCount(); case 'Air Force One': return $this->getMaxAltitude(); case 'Cessna': return $this->getMaxAltitude() - $this->getFuelExpenditure(); } } }好:interface Airplane { // ... public function getCruisingAltitude(): int; } class Boeing777 implements Airplane { // ... public function getCruisingAltitude(): int { return $this->getMaxAltitude() - $this->getPassengerCount(); } } class AirForceOne implements Airplane { // ... public function getCruisingAltitude(): int { return $this->getMaxAltitude(); } } class Cessna implements Airplane { // ... public function getCruisingAltitude(): int { return $this->getMaxAltitude() - $this->getFuelExpenditure(); } }避免类型检查(第 1 部分)PHP 是非类型化的,这意味着您的函数可以接受任何类型的参数。 有时你会被这种自由所咬,它变得很诱人 函数中的类型检查。有很多方法可以避免这样做。 首先要考虑的是一致的 API。坏:function travelToTexas($vehicle): void { if ($vehicle instanceof Bicycle) { $vehicle->pedalTo(new Location('texas')); } elseif ($vehicle instanceof Car) { $vehicle->driveTo(new Location('texas')); } }好:function travelToTexas(Vehicle $vehicle): void { $vehicle->travelTo(new Location('texas')); }避免类型检查(第 2 部分)如果您正在使用字符串、整数和数组等基本基元值, 你使用 PHP 7+,你不能使用多态性,但你仍然觉得有必要 类型检查,您应该考虑类型声明或严格模式。它为您提供了基于标准PHP语法的静态类型。 手动类型检查的问题在于,这样做需要很多 额外的措辞是,你得到的虚假“类型安全”并不能弥补损失 可读性。保持你的PHP干净,编写好的测试,并有良好的代码审查。 否则,除了PHP严格类型声明或严格模式之外,还可以执行所有这些操作。坏:function combine($val1, $val2): int { if (! is_numeric($val1) || ! is_numeric($val2)) { throw new Exception('Must be of type Number'); } return $val1 + $val2; }好:function combine(int $val1, int $val2): int { return $val1 + $val2; }删除死代码死代码和重复代码一样糟糕。没有理由保留它 您的代码库。如果它没有被调用,请摆脱它!它仍然是安全的 如果您仍然需要它,请在您的版本历史记录中。坏:function oldRequestModule(string $url): void { // ... } function newRequestModule(string $url): void { // ... } $request = newRequestModule($requestUrl); inventoryTracker('apples', $request, 'www.inventory-awesome.io');好:function requestModule(string $url): void { // ... } $request = requestModule($requestUrl); inventoryTracker('apples', $request, 'www.inventory-awesome.io');对象和数据结构使用对象封装在 PHP 中,您可以为方法设置 、 和关键字。 使用它,您可以控制对象的属性修改。public`protected`private当您想在获取对象属性之外执行更多操作时,您没有 查找和更改代码库中的每个访问器。使在执行 .set封装内部表示形式。在获取和设置时易于添加日志记录和错误处理。继承此类后,可以重写默认功能。您可以延迟加载对象的属性,例如从 服务器。此外,这是开/闭原则的一部分。坏:class BankAccount { public $balance = 1000; } $bankAccount = new BankAccount(); // Buy shoes... $bankAccount->balance -= 100;好:class BankAccount { private $balance; public function __construct(int $balance = 1000) { $this->balance = $balance; } public function withdraw(int $amount): void { if ($amount > $this->balance) { throw new \Exception('Amount greater than available balance.'); } $this->balance -= $amount; } public function deposit(int $amount): void { $this->balance += $amount; } public function getBalance(): int { return $this->balance; } } $bankAccount = new BankAccount(); // Buy shoes... $bankAccount->withdraw($shoesPrice); // Get balance $balance = $bankAccount->getBalance();使对象具有私有/受保护的成员public方法和属性对于更改来说是最危险的,因为某些外部代码可能很容易依赖它们,并且您无法控制哪些代码依赖于它们。类中的修改对类的所有用户都是危险的。protected修饰符和 public 一样危险,因为它们在任何子类的范围内都可用。这实际上意味着公共和受保护之间的区别仅在于访问机制,但封装保证保持不变。类中的修改对于所有后代类都是危险的。privatemodifier 保证代码仅在单个类的边界内修改是危险的(修改是安全的,并且不会产生 Jenga 效应)。因此,在默认情况下以及需要为外部类提供访问权限时使用。private`public/protected`有关更多信息,您可以阅读 Fabien Potencier 撰写的有关此主题的博客文章。坏:class Employee { public $name; public function __construct(string $name) { $this->name = $name; } } $employee = new Employee('John Doe'); // Employee name: John Doe echo 'Employee name: ' . $employee->name;好:class Employee { private $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $employee = new Employee('John Doe'); // Employee name: John Doe echo 'Employee name: ' . $employee->getName();类首选组合而不是继承正如四人帮在《设计模式》中所说的那样, 在可能的情况下,您应该更喜欢组合而不是继承。有很多 使用继承的充分理由和使用组合的充分理由。 这句格言的要点是,如果你的思想本能地去做 继承,试着想想组合是否可以更好地模拟你的问题。在一些 案例可以。那么,您可能想知道,“我什么时候应该使用继承?它 取决于你手头的问题,但这是一个不错的继承清单 比构图更有意义:你的继承代表一种“是”的关系,而不是“有”的关系 关系(人-动物与用户->>UserDetails)。您可以重用基类中的代码(人类可以像所有动物一样移动)。您希望通过更改基类来对派生类进行全局更改。 (改变所有动物移动时的热量消耗)。坏:class Employee { private $name; private $email; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } // ... } // Bad because Employees "have" tax data. // EmployeeTaxData is not a type of Employee class EmployeeTaxData extends Employee { private $ssn; private $salary; public function __construct(string $name, string $email, string $ssn, string $salary) { parent::__construct($name, $email); $this->ssn = $ssn; $this->salary = $salary; } // ... }好:class EmployeeTaxData { private $ssn; private $salary; public function __construct(string $ssn, string $salary) { $this->ssn = $ssn; $this->salary = $salary; } // ... } class Employee { private $name; private $email; private $taxData; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } public function setTaxData(EmployeeTaxData $taxData): void { $this->taxData = $taxData; } // ... }避免使用流畅的界面Fluent 接口是一个对象 面向 API,旨在通过使用方法链接提高源代码的可读性。虽然可能有一些上下文,通常是构建器对象,其中 pattern 降低了代码的冗长程度(例如 PHPUnit Mock Builder 或 Doctrine Query Builder), 更多时候,它需要付出一些代价:中断封装。中断装饰器。在测试套件中更难嘲笑。使提交的差异更难阅读。有关更多信息,您可以阅读 Marco Picetta 撰写的有关此主题的完整博客文章。坏:class Car { private $make = 'Honda'; private $model = 'Accord'; private $color = 'white'; public function setMake(string $make): self { $this->make = $make; // NOTE: Returning this for chaining return $this; } public function setModel(string $model): self { $this->model = $model; // NOTE: Returning this for chaining return $this; } public function setColor(string $color): self { $this->color = $color; // NOTE: Returning this for chaining return $this; } public function dump(): void { var_dump($this->make, $this->model, $this->color); } } $car = (new Car()) ->setColor('pink') ->setMake('Ford') ->setModel('F-150') ->dump();好:class Car { private $make = 'Honda'; private $model = 'Accord'; private $color = 'white'; public function setMake(string $make): void { $this->make = $make; } public function setModel(string $model): void { $this->model = $model; } public function setColor(string $color): void { $this->color = $color; } public function dump(): void { var_dump($this->make, $this->model, $this->color); } } $car = new Car(); $car->setColor('pink'); $car->setMake('Ford'); $car->setModel('F-150'); $car->dump();首选期末课程应尽可能使用关键字:final它可以防止不受控制的继承链。它鼓励作文。它鼓励单一责任原则。它鼓励开发人员使用您的公共方法,而不是扩展类来访问受保护的方法。它允许您更改代码,而不会破坏使用您的类的应用程序。唯一的条件是你的类应该实现一个接口,并且没有定义其他公共方法。有关更多信息,您可以阅读 Marco Pivetta (Ocramius) 撰写的有关此主题的博客文章。坏:final class Car { private $color; public function __construct($color) { $this->color = $color; } /** * @return string The color of the vehicle */ public function getColor() { return $this->color; } }好:interface Vehicle { /** * @return string The color of the vehicle */ public function getColor(); } final class Car implements Vehicle { private $color; public function __construct($color) { $this->color = $color; } public function getColor() { return $this->color; } }固体SOLID 是 Michael Feathers 为罗伯特·马丁 (Robert Martin) 命名的前五个原则引入的首字母缩写词,意思是面向对象编程和设计的五个基本原则。S:单一责任原则 (SRP)O:开/闭原理 (OCP)L:李氏替代原理(LSP)I:接口隔离原则(ISP)D:依赖反转原则 (DIP)单一责任原则 (SRP)正如 Clean Code 中所述,“一个类不应该有多个原因 改变”。将一个具有许多功能的类塞进包装是很诱人的,例如 当您在航班上只能携带一个行李箱时。这样做的问题是 你的班级在概念上不会有凝聚力,它会给出很多理由 来改变。尽量减少更改类所需的次数非常重要。 这很重要,因为如果一个类中有太多功能,并且您修改了其中的一部分, 可能很难理解这将如何影响其他依赖模块 您的代码库。坏:class UserSettings { private $user; public function __construct(User $user) { $this->user = $user; } public function changeSettings(array $settings): void { if ($this->verifyCredentials()) { // ... } } private function verifyCredentials(): bool { // ... } }好:class UserAuth { private $user; public function __construct(User $user) { $this->user = $user; } public function verifyCredentials(): bool { // ... } } class UserSettings { private $user; private $auth; public function __construct(User $user) { $this->user = $user; $this->auth = new UserAuth($user); } public function changeSettings(array $settings): void { if ($this->auth->verifyCredentials()) { // ... } } }开/闭原理 (OCP)正如 Bertrand Meyer 所说,“软件实体(类、模块、函数、 等)应该开放以进行扩展,但关闭以进行修改。那有什么作用 但意思是?这个原则基本上表明你应该允许用户 在不更改现有代码的情况下添加新功能。坏:abstract class Adapter { protected $name; public function getName(): string { return $this->name; } } class AjaxAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'ajaxAdapter'; } } class NodeAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'nodeAdapter'; } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { $adapterName = $this->adapter->getName(); if ($adapterName === 'ajaxAdapter') { return $this->makeAjaxCall($url); } elseif ($adapterName === 'httpNodeAdapter') { return $this->makeHttpCall($url); } } private function makeAjaxCall(string $url): Promise { // request and return promise } private function makeHttpCall(string $url): Promise { // request and return promise } }好:interface Adapter { public function request(string $url): Promise; } class AjaxAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class NodeAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { return $this->adapter->request($url); } }Liskov 替代原理 (LSP)对于一个非常简单的概念来说,这是一个可怕的术语。它的正式定义是“如果 S 是 T 的子类型,则 T 类型的对象可以替换为 S 类型的对象 (即,S 类型的对象可以替换 T 类型的对象),而不改变任何 该程序的理想属性(正确性、执行的任务、 等等)。这是一个更可怕的定义。对此最好的解释是,如果你有一个父类和一个子类, 那么基类和子类可以互换使用,而不会得到 结果不正确。这可能仍然令人困惑,所以让我们来看看 经典的 Square-Rectangle 示例。在数学上,正方形是一个矩形,但是 如果通过继承使用“is-a”关系对其进行建模,则很快 惹上麻烦了。坏:class Rectangle { protected $width = 0; protected $height = 0; public function setWidth(int $width): void { $this->width = $width; } public function setHeight(int $height): void { $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square extends Rectangle { public function setWidth(int $width): void { $this->width = $this->height = $width; } public function setHeight(int $height): void { $this->width = $this->height = $height; } } function printArea(Rectangle $rectangle): void { $rectangle->setWidth(4); $rectangle->setHeight(5); // BAD: Will return 25 for Square. Should be 20. echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()) . PHP_EOL; } $rectangles = [new Rectangle(), new Square()]; foreach ($rectangles as $rectangle) { printArea($rectangle); }好:最好的方法是将四边形分开,并为两种形状分配一个更通用的子类型。尽管正方形和矩形表面上相似,但它们是不同的。 正方形与菱形有很多共同之处,矩形与平行四边形有很多共同之处,但它们不是亚型。 正方形、矩形、菱形和平行四边形是独立的形状,具有各自的属性,尽管相似。interface Shape { public function getArea(): int; } class Rectangle implements Shape { private $width = 0; private $height = 0; public function __construct(int $width, int $height) { $this->width = $width; $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square implements Shape { private $length = 0; public function __construct(int $length) { $this->length = $length; } public function getArea(): int { return $this->length ** 2; } } function printArea(Shape $shape): void { echo sprintf('%s has area %d.', get_class($shape), $shape->getArea()).PHP_EOL; } $shapes = [new Rectangle(4, 5), new Square(5)]; foreach ($shapes as $shape) { printArea($shape); }接口隔离原则 (ISP)ISP 指出,“不应强迫客户端依赖于 他们不使用。一个很好的例子可以证明这个原则是 需要大型设置对象的类。不需要客户端设置 大量的选择是有益的,因为大多数时候他们不需要 所有设置。使它们成为可选有助于防止出现“胖接口”。坏:interface Employee { public function work(): void; public function eat(): void; } class HumanEmployee implements Employee { public function work(): void { // ....working } public function eat(): void { // ...... eating in lunch break } } class RobotEmployee implements Employee { public function work(): void { //.... working much more } public function eat(): void { //.... robot can't eat, but it must implement this method } }好:不是每个工人都是雇员,但每个雇员都是工人。interface Workable { public function work(): void; } interface Feedable { public function eat(): void; } interface Employee extends Feedable, Workable { } class HumanEmployee implements Employee { public function work(): void { // ....working } public function eat(): void { //.... eating in lunch break } } // robot can only work class RobotEmployee implements Workable { public function work(): void { // ....working } }依赖关系反转原则 (DIP)该原则规定了两件基本的事情:高级模块不应依赖于低级模块。两者都应该 依赖于抽象。抽象不应依赖于细节。细节应取决于 抽象。一开始可能很难理解,但如果你使用过PHP框架(如Symfony),你就会看到这个原则以依赖的形式实现 注射 (DI)。虽然它们不是相同的概念,但 DIP 保持了高层次 模块,了解其低级模块的详细信息并设置它们。 它可以通过 DI 来实现这一点。这样做的一个巨大好处是它减少了 模块之间的耦合。耦合是一种非常糟糕的开发模式,因为 它使你的代码难以重构。坏:class Employee { public function work(): void { // ....working } } class Robot extends Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }好:interface Employee { public function work(): void; } class Human implements Employee { public function work(): void { // ....working } } class Robot implements Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }不要重复自己 (DRY)尽量遵守 DRY 原则。尽最大努力避免重复代码。重复代码很糟糕,因为 这意味着如果需要,有不止一个地方可以更改某些内容 改变一些逻辑。想象一下,如果你经营一家餐馆,你跟踪你的库存:所有的 西红柿、洋葱、大蒜、香料等。如果您有多个列表 你保持这个状态,那么当你端上一道菜时,一切都必须更新 西红柿在里面。如果您只有一个列表,则只有一个位置可以更新!通常,您有重复的代码,因为您有两个或更多代码 不同的东西,有很多共同点,但它们的差异迫使你 具有两个或多个单独的函数,它们执行许多相同的操作。删除 重复代码意味着创建一个抽象,可以处理这组不同的 只有一个函数/模块/类的东西。获得正确的抽象是至关重要的,这就是为什么你应该遵循 “类”部分中列出的 SOLID 原则。糟糕的抽象可能是 比重复代码更糟糕,所以要小心!话虽如此,如果你能做到 一个好的抽象,去做吧!不要重复自己,否则你会发现自己 随时更新多个位置,想要更改一件事。坏:function showDeveloperList(array $developers): void { foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); $data = [$expectedSalary, $experience, $githubLink]; render($data); } } function showManagerList(array $managers): void { foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); $data = [$expectedSalary, $experience, $githubLink]; render($data); } }好:function showList(array $employees): void { foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); $data = [$expectedSalary, $experience, $githubLink]; render($data); } }非常好:最好使用代码的紧凑版本。function showList(array $employees): void { foreach ($employees as $employee) { render([$employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink()]); } }
2023年12月11日
9 阅读
0 评论
0 点赞
1
...
32
33
34
...
112