Deprecated
: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in
/www/wwwroot/testblog.58heshihu.com/var/Widget/Archive.php
on line
1057
首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
182 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
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
累计撰写
786
篇文章
累计收到
28
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
2
篇与
的结果
2023-12-29
代码重构三个原则和技巧
代码重构三个原则和技巧随着业务需求不断改变和代码规模的扩大,PHP应用程序中出现代码重复和冗余的问题是很常见的。代码重复和冗余不仅增加了代码的维护成本,也会降低代码的可读性和可维护性。如果不采取有效的措施解决这些问题,最终可能会导致系统稳定性和应用程序的性能问题。在本文中,我们将探讨使用代码重构技巧来消除PHP代码中的重复和冗余的方法。我们将深入研究代码重构的原则和技巧,并提供有关如何重构PHP代码的实用示例。我们的目标是提高PHP应用程序的可读性,可维护性和性能,同时降低代码维护成本。原则和技巧在进行代码重构之前,请仔细思考以下原则和技巧。这些原则和技巧将帮助您快速准确地确定哪些代码需要重构,以及应该如何重构。1.遵循DRY原则DRY原则,即“不要重复自己”的原则,是代码重构的核心原则。DRY原则强调的是,在代码中尽可能避免重复和冗余,并且尽量将复杂逻辑抽象为可重用的组件。当应用程序中出现重复和冗余代码时,往往需要同时修改多个地方,这将耗费大量的时间和精力,并且还会导致出现错误的可能。因此,遵循DRY原则非常重要,可以有效地减少代码重复和冗余,并提高代码的可读性和可维护性。2.使用函数和类使用函数和类是遵循DRY原则的最佳方式,因为它们可以抽象出可重用的代码段,将复杂逻辑封装起来,减少代码冗余,并提高代码的可读性和可维护性。通过函数和类,可以将重复和冗余的代码分离出来,并将它们放在一个单独的代码块中,从而方便维护。3.采用命名规范在重构代码时,为变量、函数和类采用清晰、简洁的命名规范非常重要。这有助于代码的可读性和可维护性,并且可以减少因代码混淆而带来的不必要问题。通常,命名规范应该尽可能地简洁明了,包含有意义的名称,避免使用简写或缩写,以及避免使用具有多种含义的词汇。使用一致的命名规范还可以方便代码的维护和追踪。示例下面我们来看几个实际的示例,说明如何使用代码重构来消除PHP代码中的重复和冗余。1.使用函数和类假设我们有一个以下代码,用于计算两个日期之间的天数:$startDate = strtotime('2021-01-01'); $endDate = strtotime('2021-01-31'); $days = floor(($endDate - $startDate) / 86400); echo "Days between: $days";在这个例子中,我们使用了strtotime()函数来将字符串日期转换为时间戳,并且使用86400来表示一天的秒数。但是,这个计算天数的代码在不同的地方会被重复使用,因此它违反了DRY原则。为了消除这种重复,我们可以将它封装在一个单独的函数中:function calculateDaysBetween($startDate, $endDate) { $days = floor(($endDate - $startDate) / 86400); return $days; } $startDate = strtotime('2021-01-01'); $endDate = strtotime('2021-01-31'); $days = calculateDaysBetween($startDate, $endDate); echo "Days between: $days";在这个例子中,我们将计算天数的逻辑封装在了一个名为calculateDaysBetween()的函数中,这样我们可以在需要时轻松地调用它,而无需重复编写相同的逻辑。通过这种重构方式,我们遵循了DRY原则,并且提高了代码的可读性、可维护性和可重用性。2.减少条件语句在很多PHP应用程序中,条件语句可能会变得非常冗长和复杂。在这种情况下,当需要对代码进行修改和维护时,它们会变得非常难以理解。下面是一个简单的示例,用于在条件语句中使用逻辑或运算符:if($a == 1 || $a == 2 || $a == 3) { // do something }在这个示例代码中,使用逻辑或运算符连接了多个条件语句。这种代码可能很难理解和维护,因此我们可以使用in_array()函数来简化条件语句:if(in_array($a, [1, 2, 3])) { // do something }通过这种重构方式,我们使用一个in_array()函数来代替了冗长的条件语句,使代码更易于理解和维护。3.引入配置文件在PHP应用程序中,可能会使用很多硬编码的字符串和常量。这些字符串和常量通常用于定义数据库连接信息、网站地址等。下面是一个简单的示例,展示如何使用硬编码的字符串来定义数据库连接信息:$link = mysqli_connect("localhost", "admin", "123456", "my_db");这样的代码可能很难维护,因为这些硬编码的字符串分散在不同的代码文件中,并且可能需要在多个地方进行修改。为了消除这种冗余,我们可以使用一个单独的配置文件来定义这些字符串和常量。在这个配置文件中,我们可以定义一个数组,包含了所有的连接信息,如下所示:$config = [ 'db_host' => 'localhost', 'db_user' => 'admin', 'db_pass' => '123456', 'db_name' => 'my_db' ]; 然后,在代码中,我们可以使用这个配置数组来定义和连接数据库,如下所示:$link = mysqli_connect($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']);通过这种重构方式,我们将数据库连接信息定义在单独的配置文件中,从而避免了代码重复和冗余,并且使得这些信息更容易维护和修改。结论在本文中,我们介绍了一些有效的代码重构技巧,以消除PHP应用程序中的重复和冗余。使用这些技巧,我们可以通过遵循DRY原则,使用函数和类,减少条件语句和引入配置文件等方法来提高代码的可读性、可维护性和性能,并且降低代码维护成本。在实际开发中,代码重构是一个非常重要的任务。通过持续地优化和重构代码,我们可以使其更加精简、高效,提高代码的质量和可维护性,从而使我们的应用程序更加稳定、可靠。
2023年12月29日
12 阅读
0 评论
0 点赞
2023-12-28
“重构” PHP 代码!
“重构” PHP 代码!重构指的是在不改变原有功能的情况下,修改或者重新编写代码。下面的例子中,我将向你展示如何更好地编写代码。1 - 表现力这可能只是一个简单的技巧,但编写富有表现力的代码可以大大改进我们的代码。总是让代码自我解释,这样未来的你或其他开发人员都能知道代码中发生了什么。不过也有开发人员表示,命名是编程中最困难的事情之一。这就是为什么这不像听起来那么容易的原因之一。示例 #1 - 命名之前// ❌ 这个方法是用来做什么的,方法名表达并不清晰 // ❌ 是设置状态还是检查状态呢? $status = $user->status('pending');之后// ✅ 通过添加 is,使方法名表达的意图更清晰 // ✅ 检测用户状态是否与给定状态相等 // ✅ 同时新变量名让我们可以推断它是布尔值 $isUserPending = $user->isStatus('pending');示例 #2 - 命名之前// ❌ 这个类返回的是什么?类名?类全名?还是类路径? return $factory->getTargetClass();之后// ✅ 我们获取的是类路径 // ✅ 如果用户想要类名?则找错了方法 return $factory->getTargetClassPath();示例 #3 - 提取之前// ❌ 重复的代码 ( "file_get_contents", "base_path" 方法以及文件扩展) // ❌ 此刻,我们不去关心如何获得code examples public function setCodeExamples(string $exampleBefore, string $exampleAfter) { $this->exampleBefore = file_get_contents(base_path("$exampleBefore.md")); $this->exampleAfter = file_get_contents(base_path("$exampleAfter.md")); }之后public function setCodeExamples(string $exampleBefore, string $exampleAfter) { // ✅ 代码直接说明了我们的意图:获取code example(不关注如何获取) $this->exampleBefore = $this->getCodeExample($exampleBefore); $this->exampleAfter = $this->getCodeExample($exampleAfter); } // ✅ 这个新方法可多次调用 private function getCodeExample(string $exampleName): string { return file_get_contents(base_path("$exampleName.md")); }示例 #4 - 提取之前// ❌ 多重 where 语句,使阅读变得困难 // ❌ 意图究竟是什么呢? User::whereNotNull('subscribed')->where('status', 'active');之后// ✅ 这个新的scope方法说明了发生了什么事 // ✅ 如果我们需要了解更多细节,可以进入这个scope方法内部去了解 // ✅ "subscribed" scope 方法可在其他地方使用 User::subscribed();示例 #5 - 提取这是我之前项目的一个例子。我们用命令行导入用户。ImportUsersCommand 类中含有一个 handle 方法,用来处理任务。之前protected function handle() { // ❌ 这个方法包含太多代码 $url = $this->option('url') ?: $this->ask('Please provide the URL for the import:'); $importResponse = $this->http->get($url); // ❌ 进度条对用户很有用,不过却让代码显得杂乱 $bar = $this->output->createProgressBar($importResponse->count()); $bar->start(); $this->userRepository->truncate(); collect($importResponse->results)->each(function (array $attributes) use ($bar) { $this->userRepository->create($attributes); $bar->advance(); }); // ❌ 很难说清此处发生了哪些行为 $bar->finish(); $this->output->newLine(); $this->info('Thanks. Users have been imported.'); if($this->option('with-backup')) { $this->storage ->disk('backups') ->put(date('Y-m-d').'-import.json', $response->body()); $this->info('Backup was stored successfully.'); } }之后protected function handle(): void { // ✅ handle方法是你访问该类首先会查看的方法 // ✅ 现在可以很容易就对这个方法做了些什么有个粗略的了解 $url = $this->option('url') ?: $this->ask('Please provide the URL for the import:'); $importResponse = $this->http->get($url); $this->importUsers($importResponse->results); $this->saveBackupIfAsked($importResponse); } // ✅ 如果需要了解更多细节,可以查看这些专用的方法 protected function importUsers($userData): void { $bar = $this->output->createProgressBar(count($userData)); $bar->start(); $this->userRepository->truncate(); collect($userData)->each(function (array $attributes) use ($bar) { $this->userRepository->create($attributes); $bar->advance(); }); $bar->finish(); $this->output->newLine(); $this->info('Thanks. Users have been imported.'); } // ✅ 不要害怕使用多行代码 // ✅ 这个例子中它让我们核心的 handle 方法更为简洁 protected function saveBackupIfAsked(Response $response): void { if($this->option('with-backup')) { $this->storage ->disk('backups') ->put(date('Y-m-d').'-import.json', $response->body()); $this->info('Backup was stored successfully.'); } }2 - 提前返回提前返回指的是,我们尝试通过将结构分解为特定 case 来避免嵌套的做法。这样,我们得到了更线性的代码,更易于阅读和了解。不要害怕使用多个 return 语句。示例 #1之前public function calculateScore(User $user): int { if ($user->inactive) { $score = 0; } else { // ❌ 怎么又有一个 "if"? if ($user->hasBonus) { $score = $user->score + $this->bonus; } else { // ❌ 由于存在多个层级,大费眼神 ? $score = $user->score; } } return $score; }之后public function calculateScore(User $user): int { // ✅ 边缘用例提前检测 if ($user->inactive) { return 0; } // ✅ 每个用例都有自己的代码块,使得更容易跟进 if ($user->hasBonus) { return $user->score + $this->bonus; } return $user->score; }示例 #2之前public function sendInvoice(Invoice $invoice): void { if($user->notificationChannel === 'Slack') { $this->notifier->slack($invoice); } else { // ❌ 即使是简单的ELSE都影响代码的可读性 $this->notifier->email($invoice); } }之后public function sendInvoice(Invoice $invoice): bool { // ✅ 每个条件都易读 if($user->notificationChannel === 'Slack') { return $this->notifier->slack($invoice); } // ✅ 不用再考虑ELSE 指向哪里 return $this->notifier->email($invoice); }Note: 有时你会听到 “防卫语句” 这样的术语,它是通过提前返回实现。3 - 重构成集合 Collection在 PHP 中,我们在很多不同数据中都用到了数组。处理及转换这些数组可用功能非常有限,并且没有提供良好的体验。(array_walk, usort, etc)要处理这个问题,有一个 Collection 类的概念,可用于帮你处理数组。最为人所知的是 Laravel 中的实现,其中的 collection 类提供了许多有用的特性,用来处理数组。注意: 以下例子, 我将使用 Laravel 的 collect () 辅助函数,不过在其他框架或库中的使用方式也很相似。示例 #1之前// ❌ 这里我们有一个临时变量 $score = 0; // ❌ 用循环没有问题,不过可读性还是有改善空间 foreach($this->playedGames as $game) { $score += $game->score; } return $score;之后// ✅ 集合是带有方法的对象 // ✅ sum 方法使之更具表现力 return collect($this->playedGames) ->sum('score');示例 #2之前$users = [ [ 'id' => 801, 'name' => 'Peter', 'score' => 505, 'active' => true], [ 'id' => 844, 'name' => 'Mary', 'score' => 704, 'active' => true], [ 'id' => 542, 'name' => 'Norman', 'score' => 104, 'active' => false], ]; // 请求结果: 只显示活跃用户,以 score 排序 ["Mary(704)","Peter(505)"] $users = array_filter($users, fn ($user) => $user['active']); // ❌ usort 进行排序处理的又是哪一个对象呢?它是如何实现? usort($users, fn($a, $b) => $a['score'] < $b['score']); // ❌ 所有的转换都是分离的,不过都是users相关的 $userHighScoreTitles = array_map(fn($user) => $user['name'] . '(' . $user['score'] . ')', $users); return $userHighScoreTitles;之后$users = [ [ 'id' => 801, 'name' => 'Peter', 'score' => 505, 'active' => true], [ 'id' => 844, 'name' => 'Mary', 'score' => 704, 'active' => true], [ 'id' => 542, 'name' => 'Norman', 'score' => 104, 'active' => false], ]; // 请求结果: 只显示活跃用户,以 score 排序 ["Mary(704)","Peter(505)"] // ✅ 只传入一次users return collect($users) // ✅ 我们通过管道将其传入所有方法 ->filter(fn($user) => $user['active']) ->sortBy('score') ->map(fn($user) => "{$user['name']} ({$user['score']})" ->values() // ✅ 最后返回数组 ->toArray();4 - 一致性每一行代码都会增加少量的视觉噪音。代码越多,阅读起来就越困难。这就是为什么制定规则很重要。保持类似的东西一致将帮助您识别代码和模式。这将导致更少的噪声和更可读的代码。示例 #1之前class UserController { // ❌ 确定如何命名变量(驼峰或是蛇形等),不要混用! public function find($userId) { } } // ❌ 选择使用单数或者复数形式命名控制器,并保持一致 class InvoicesController { // ❌ 修改了样式,如花扣号的位置,影响可读性 public function find($user_id) { } }之后class UserController { // ✅ 所有变量驼峰式命名 public function find($userId) { } } // ✅ 控制器命名规则一致(此处都使用单数) class InvoiceController { // ✅ 花括号的位置(格式)一致,使代码更为可读 public function find($userId) { } }示例 #2之前class PdfExporter { // ❌ "handle" 和 "export" 是类似方法的不同名称 public function handle(Collection $items): void { // export items... } } class CsvExporter { public function export(Collection $items): void { // export items... } } // ❌ 使用时你会疑惑它们是否处理相似的任务 // ❌ 你可能需要再去查看类源码进行确定 $pdfExport->handle(); $csvExporter->export();之后// ✅ 可通过接口提供通用规则保持一致性 interface Exporter { public function export(Collection $items): void; } class PdfExporter implements Exporter { public function export(Collection $items): void { // export items... } } class CsvExporter implements Exporter { public function export(Collection $items): void { // export items... } } // ✅ 对类似的任务使用相同的方法名,更具可读性 // ✅ 不用再去查看类源码,变可知它们都用在导出数据 $pdfExport->export(); $csvExporter->export();重构 ❤️ 测试我已经提到过重构不会改变代码的功能。这在运行测试时很方便,因为它们也应该在重构之后工作。这就是为什么我只有在有测试的时候才开始重构代码。他们将确保我不会无意中更改代码的行为。所以别忘了写测试,甚至去 TDD。
2023年12月28日
15 阅读
0 评论
0 点赞