首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
183 阅读
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
篇文章
累计收到
31
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
560
篇与
的结果
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 点赞
2023-12-04
php接口优化 使用curl_multi_init批量请求
需求:一个接口里请求了多个接口(方法),查询速度缓慢,每个请求2s,加起来11、12s。实现:PHP中的curl_multi一类函数可以实现同时请求多个url,而不是一个一个依次请求,这就类似一个进程实现了多个线程的功能,最终接口请求耗时优化到2s,即查询耗时最长的那次是最终的查询时长。<?php class XClass extends BaseSys { public function index() { $rs = []; $local_url = "当前请求地址"; $urlArr = []; $paramsArr = []; $params = $_POST; $curAction = [ ['name'=>'getANum','bool'=>''], ['name'=>'getCNum','bool'=>true], ['name'=>'getCNum','bool'=>false], ['name'=>'getLNum','bool'=>''], ...... ];//接口名、参数 foreach ($curAction as $key => $item) { $urlArr[] = $local_url . "XClass/".$item["name"]; $subparams = $params; $subparams["bool"] = $item["bool"]; $subparams["isMutil"] = 1; $paramsArr[] = $subparams; } $contentArr = $this->mutil_curl($urlArr, $paramsArr); foreach ($contentArr as &$item) { $rs[] = json_decode($item,true); } return $rs; } public function getCNum($isNew = false) { //TODO if (isset($_POST["isMutil"])) {//mutil_curl并发请求结果 exit($json_encode($rs, JSON_UNESCAPED_UNICODE) . ''); } return $rs; } /** * 多url同时访问curl操作封装,一般用于内部并行数据查询 * $urlArr 要访问的网址集合,get方法的时候请在此网址上直接带上参数 * $paramsArr 要post的数据集合 */ public function mutil_curl($urlArr, $paramsArr = null, $timeout = 60) { $contentArr = []; $num = 0; $len = count($urlArr)-1; foreach ($urlArr as $k => $url) { if($num == 0){ $subrulArr = []; } $subrulArr[$k] = $url; $num++; if($num == 16 || $len == $k){ $subcontentArr = $this->_do_mutil_curl($subrulArr, $paramsArr); foreach ($subcontentArr as $subkey => $subvalue) { $contentArr[$subkey] = $subvalue; } $num = 0; } } return $contentArr; } public function _do_mutil_curl($urlArr, $paramsArr = null, $timeout = 60) { set_time_limit($timeout); // curl 批处理 $mh = curl_multi_init(); // 保存单个句柄 $handles = []; // 向 curl 批处理会话 中添加单独的 curl句柄 foreach ($urlArr as $k => $url) { $ch = curl_init(); // 初始化curl并设置链接 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // 设置是否为post传递 curl_setopt($ch, CURLOPT_POST, !empty($paramsArr)); // 对于https 设定为不验证证书和host curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//设置是否返回信息 if (!empty($paramsArr)) { $params = $paramsArr[$k]; if ($params) { if (is_array($params)) { $params = http_build_query($params); } // POST 数据 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); } } $handles[$k] = $ch; curl_multi_add_handle($mh, $handles[$k]); } $active = null; $contentArr = []; do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active and $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // 循环输出 大小以及地址 foreach ($handles as $i => $handle) { // 内容 $content = curl_multi_getcontent($handle); $contentArr[$i] = $content; curl_multi_remove_handle($mh, $handle); } curl_multi_close($mh); return $contentArr; } PS:这里为了方便显示,把mutil_curl()和_do_mutil_curl()放在同一个类中,实际上它们是放在一个工具类里,方便调用。
2023年12月04日
144 阅读
6 评论
0 点赞
2023-11-30
mysql报错记录:ERROR 1055 (42000): sql_mode = only_full_group_by不兼容
ERROR 1055 (42000): sql_mode = only_full_group_by不兼容mysql> select sum(id),name from student group by name; +---------+--------+ | sum(id) | name | +---------+--------+ | 1 | 张三 | | 2 | 尔四 | | 3 | 小红 | | 10 | 小明 | | 5 | 小青 | +---------+--------+ 5 rows in set (0.00 sec) mysql> select sum(id),name,code from student group by name; ERROR 1055 (42000): Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.student.code' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by 错误描述:“错误代码:1055,SELECT列表的表达式 #1 不在GROUP BY子句中,并且包含非聚合列’test.student.code’,它在功能上不依赖于GROUP BY子句中的列; 这与sql_mode = only_full_group_by不兼容”分析问题1)原理层面这个错误会发生在mysql 5.7 版本及以上版本mysql 5.7版本以上默认的sql配置是:sql_mode=“ONLY_FULL_GROUP_BY”,这个配置严格执行了"SQL92标准"。很多程序员从5.6升级到5.7时,为了语法兼容,大部分会选择调整sql_mode,使其保持跟5.6一致,为了尽量兼容程序。2)sql层面在sql执行时,出现该原因,简单来说就是:由于开启了ONLY_FULL_GROUP_BY的设置,如果select 的字段不在 group by 中,并且select 的字段未使用聚合函数(SUM,AVG,MAX,MIN等)的话,那么这条sql查询是被mysql认为非法。
2023年11月30日
20 阅读
0 评论
0 点赞
2023-11-28
关于mysql的lower_case_table_names引发的思考
lower_case_table_names设置大小写敏感的三个值0、1、2的区别?lower_case_table_names参数详解1.参数说明:lower_case_table_names= 0 表名 存储为给定的大小和比较是区分大小写的lower_case_table_names = 1 表名 存储在磁盘是小写的 ,但是 比较的时候是不区分大小写lower_case_table_names= 2 表名 存储为给定的大小写 但是 比较的时候是小写的unix,linux下lower_case_table_names默认值为 0 .Windows下默认值是 1 .Mac OS X下默认值是 22.查看方法:# 进入mysql命令行 执行以下任一语句查看:show variables like 'lower_case_table_names';select @@lower_case_table_names;3.更改方法:更改数据库参数文件 my.cnf 在mysqld下 添加或修改 lower_case_table_names = 1 之后重启数据库4.现实情况修改 注意事项:因目前MySQL安装在 Linux 系统上较多 初始化时采取了默认的lower_case_table_names值 即区分大小写,后续可能会造成同一实例大小写库表都存在的情况,调用时还要注意大小写。这时 更改步骤如下:1.核实实例中是否存在大写的库及表2.将大写的库名及表名改为小写更改库名可参考 : MySQL数据库改名的三种方法更改表名:rename table TEST_TB to test_tb;3.设置lower_case_table_names = 14.重启数据库
2023年11月28日
33 阅读
0 评论
0 点赞
2023-11-28
MySQL数据库改名的三种方法
如果表是 MyISAM 那么可以直接去到数据库目录 mv 就可以。Innodb完全不行,自己测试过,会提示相关表不存在。第一种方法:RENAME database olddbname TO newdbname这个语法在 mysql-5.1.7 中被添加进来,到了mysql-5.1.23又去掉了,官方不推荐,会有丢失数据的危险!第二种方法:1.创建需要改成新名的数据库。2.mysqldum 导出要改名的数据库3.删除原来的旧库(确定是否真的需要)当然这种方法虽然安全,但是如果数据量大,会比较耗时。#创建数据库 CREATE DATABASE IF NOT EXISTS `库名` default charset utf8mb4 COLLATE utf8mb4_unicode_ci; # 将db1库备份到db1.sql文件 mysqldump -u root -p db1 > /tmp/db1.sql; #Enter password: #[root@xxx ~]# # 导入备份文件到新库db2 mysql -u root -p db2 < /tmp/db1.sql; #Enter password: #[root@xxx ~]# # 删除旧库(如果真的需要) DROP DATABASE db1;第三种方法:我这里就用一个脚本,很简单,相信大家都看的懂 newdatabase.sh#!/bin/bash # 假设将db1数据库名改为db2 # MyISAM直接更改数据库目录下的文件即可 # 下面脚本是创建新数据库,获取旧数据库所有表名,将其循环移动到新数据库 mysql -uroot -p123456 -e 'create database if not exists db2' list_table=$(mysql -uroot -p123456 -Nse "select table_name from information_schema.TABLES where TABLE_SCHEMA='db1'") for table in $list_table do mysql -uroot -p123456 -e "rename table db1.$table to db2.$table" done-p123456脚本中把明文密码暴露出来。。。[root@xxx script]# mysql --version mysql Ver 8.0.35 for Linux on x86_64 (MySQL Community Server - GPL) [root@xxx script]# bash newdatabase.sh mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure. #mysql:[警告]在命令行界面使用密码可能是不安全的。 执行成功了但不安全,我们做下调整: 生成密文 sginpwd.sh#!/bin/bash function b64Code() { #参数1: 需要加密的串 passauth=$1 #参数2: 加密的次数,次数越多密码越长 for i in `seq 1 $2` do #python2 passauth=`echo $passauth |python -c "import base64;s=raw_input();print(base64.b64encode(s))"` #python3 #passauth=`echo $passauth |python -c "import base64;s=input(); print(base64.b64encode(s.encode()).decode());"` echo "[+]第${i}次加密结果:$passauth" done } #执行 b64Code 加密方法,使用明文密码加密10次 b64Code 123456 10 #密码是字符串加上单引号执行sginpwd.sh[root@xxx script]# bash sginpwd.sh [+]第1次加密结果:MTIzNDU2 [+]第2次加密结果:TVRJek5EVTI= [+]第3次加密结果:VFZSSmVrNUVWVEk9 [+]第4次加密结果:VkZaU1NtVnJOVVZXVkVrOQ== [+]第5次加密结果:VmtaYVUxTnRWbkpPVlZaWFZrVnJPUT09 [+]第6次加密结果:Vm10YVlWVXhUblJXYmtwUFZsWmFXRlpyVm5KUFVUMDk= [+]第7次加密结果:Vm0xMFlWbFdWWGhVYmxKWFltdHdVRlpzV21GWFJscHlWbTVLVUZWVU1Eaz0= [+]第8次加密结果:Vm0weE1GbFdiRmRXV0doVllteEtXRmx0ZEhkVlJscHpWMjFHV0ZKc2NIbFdiVFZMVlVaV1ZVMUVhejA9 [+]第9次加密结果:Vm0wd2VFMUdiRmRpUm1SWFYwZG9WbGx0ZUV0WFJteDBaRWhrVmxKc2NIcFdNakZIVjBaS2MyTkliRmRpVkZaTVZsVmFWMVpWTVVWaGVqQTk= [+]第10次加密结果:Vm0wd2QyVkZNVWRpUm1ScFVtMVNXRll3Wkc5V2JHeDBaVVYwV0ZKdGVEQmFSV2hyVm14S2MyTkljRmROYWtaSVZqQmFTMk15VGtsaVJtUnBWa1phVFZac1ZtRldNVnBXVFZWV2FHVnFRVGs9newdatabase_beff.sh#!/bin/bash # 假设将db1数据库名改为db2 # MyISAM直接更改数据库目录下的文件即可 # 下面脚本是创建新数据库,获取旧数据库所有表名,将其循环移动到新数据库 function deCode() { #参数1: 加密串内容 passDstr=$1 #参数2: 密码解析次数 for i in `seq 1 $2` do passDstr=`echo $passDstr |python -c "import base64;s=input(); print(base64.b64decode(s.encode()).decode());"` done } #b64Code方法得到的密文放在这里 mysqlPass="Vm0wd2QyVkZNVWRpUm1ScFVtMVNXRll3Wkc5V2JHeDBaVVYwV0ZKdGVEQmFSV2hyVm14S2MyTkljRmROYWtaSVZqQmFTMk15VGtsaVJtUnBWa1phVFZac1ZtRldNVnBXVFZWV2FHVnFRVGs9" #使用加密10次的密文 解密 deCode $mysqlPass 10 mysql -uroot -p"${passDstr}" -e "show databases;" mysql -uroot -p"${passDstr}" -e 'create database if not exists db2' list_table=$(mysql -uroot -p"${passDstr}" -Nse "select table_name from information_schema.TABLES where TABLE_SCHEMA='db1'") for table in $list_table do mysql -uroot -p"${passDstr}" -e "rename table db1.$table to db2.$table" done function deCode() { #参数1: 加密串内容 passDstr=$1 #参数2: 密码解析次数 for i in `seq 1 $2` do #python2 passDstr=`echo $passDstr |python -c "import base64;s=raw_input();print(base64.b64decode(s))"` #python3 #passDstr=`echo $passDstr |python -c "import base64;s=input(); print(base64.b64decode(s.encode()).decode());"` done } #b64Code方法得到的密文放在这里 mysqlPass="Vm0wd2QyVkZNVWRpUm1ScFVtMVNXRll3Wkc5V2JHeDBaVVYwV0ZKdGVEQmFSV2hyVm14S2MyTkljRmROYWtaSVZqQmFTMk15VGtsaVJtUnBWa1phVFZac1ZtRldNVnBXVFZWV2FHVnFRVGs9" #使用加密10次的密文 解密 deCode $mysqlPass 10 mysql -uroot -p"${passDstr}" -e "show databases;" mysql -uroot -p"${passDstr}" -e 'create database if not exists db2' list_table=$(mysql -uroot -p"${passDstr}" -Nse "select table_name from information_schema.TABLES where TABLE_SCHEMA='db1'") for table in $list_table do mysql -uroot -p"${passDstr}" -e "rename table db1.$table to db2.$table" done[root@xxx script]# bash newdatabase_beff.sh mysql: [Warning] Using a password on the command line interface can be insecure. +--------------------+ | Database | +--------------------+ | db1 | | db2 | +--------------------+ mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure. #登录mysql,可以看到db1数据表迁移到db2了 mysql> use db1; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +---------------+ | Tables_in_db1 | +---------------+ | active_log | | new | +---------------+ 2 rows in set (0.00 sec) mysql> show tables; Empty set (0.00 sec) mysql> use db2; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +---------------+ | Tables_in_db2 | +---------------+ | active_log | | new | +---------------+ 2 rows in set (0.00 sec)这里用到了rename table,改表名的命令,但是如果新表名后面加数据库名,就会将老数据库的表移动到新的数据库,所以,这种方法即安全,又快速。参考链接: https://ost.51cto.com/posts/16797 https://blog.csdn.net/China_AT001/article/details/116916740 番外:所有表是 MyISAM 修改数据库名1.先停止数据库服务systemctl stop mysqld #service mysql stop 2.到数据库目录 mv 数据库目录默认在/var/lib/mysql/,可以通过 find / -name mysql 查找mv /var/lib/mysql/old_database /var/lib/mysql/new_database3.启动数据库服务systemctl start mysqld #service mysql start4.登录mysql,如果显示new_database数据库,说明修改成功数据库名了show databases;最终结果目录改了,数据库没显示,操作失败不知道啥原因,以后再看吧
2023年11月28日
12 阅读
0 评论
0 点赞
1
...
33
34
35
...
112