- Kirish
- O'zgaruvchilar
- Ma'noga ega va tushunarli nomlardan foydalaning
- Bir xil turdagi o'zgaruvchilar uchun xuddi shunday nomdan foydalaning
- Izlashga qulay bo'lgan nomlardan foydalaning (1-qism)
- Izlashga qulay bo'lgan nomlardan foydalaning (2-qism)
- Mantiqni izohlaydigan o'zgaruvchilardan foydalaning
- Shart operatorlarini bir birini ichiga chuqur joylashtirish va qiymatlarni erta qaytarishni oldini oling (1-qism)
- Shart operatorlarini bir birini ichiga chuqur joylashtirish va qiymatlarni erta qaytarishni oldini oling (2-qism)
- O'zgaruvchilarni nomlashda Aqliy Xaritalamang
- Keraksiz kontekst qo'shmang
- Qisqa formalar yoki shartlar o'rniga standart argumentlardan foydalaning
- Taqqsolash
- Funksiyalar
- Funksiya argumentlari (2 yoki kamrog'i ideal)
- Funksiya nomi qanday amal bajarilayotganini aytishi kerak
- Funksiyalar bir darajada abstrakt bo'lishi kerak
- Funksiyada mantiqiy toifalardan parametr sifatida foydalanmang
- Yondosh ta'sirlarni oldini oling
- Global funksiyalarga yozmang
- Singleton pattern'dan foydalanmang
- Shartlarni inkapsulatsiyalash
- Inkor qiluvchi shartlardan foydalanmang
- Shartlarni oldini oling
- Toifani tekshirishni oldini oling (1-qism)
- Toifani tekshirishni oldini oling (2-qism)
- O'lik kodni olib tashlang
- Obyektlar va ma'lumotlar tuzilmasi
- Sinflar
- SOLID
- Yagona javobgarlik printspi (Single Responsibility Principle, SRP)
- Ochiq/Yopiqlik printspi (Open/Closed Principle, OCP)
- Liskov almashtirish printsipi (Liskov Substitution Principle, LSP)
- Interfeysni ajratish printsipi (Interface Segregation Principle, ISP)
- Bog'liqlik inversiyasi printsipi (Dependency Inversion Principle, DIP)
- O'zingizni takrorlamang (Don’t repeat yourself, DRY)
- Tarjimalar
Software engineering principles, from Robert C. Martin's book Clean Code, adapted for PHP. This is not a style guide. It's a guide to producing readable, reusable, and refactorable software in PHP.
Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of Clean Code.
Inspired from clean-code-javascript.
Although many developers still use PHP 5, most of the examples in this article only work with PHP 7.1+.
Yomon:
declare(strict_types=1);
$ymdstr = $moment->format('y-m-d');
Yaxshi:
declare(strict_types=1);
$currentDate = $moment->format('y-m-d');
Yomon:
declare(strict_types=1);
getUserInfo();
getUserData();
getUserRecord();
getUserProfile();
Yaxshi:
declare(strict_types=1);
getUser();
Biz kod yozishdan ko'ra ko'proq ularni o'qiymiz. Shuning uchun biz yozadigan kod izlashga va o'qishga qulay bo'lishi muhimdir. Dasturimizni tushunishda ahamaiyatga ega bo'lgan o'zgaruvchilarni ma'noli qilib nomlamaslik, kodni o'qiyotgan dasturchiga qiyinchilik tug'diradi. O'zgaruvchilarni izlashga qulay qilib nomlang.
Yomon:
declare(strict_types=1);
// 448 nimani anglatadi?
$result = $serializer->serialize($data, 448);
Yaxshi:
declare(strict_types=1);
$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
Yomon:
declare(strict_types=1);
class User
{
// 7 nimani anglatadi ?
public $access = 7;
}
// 4 nimani anglatadi?
if ($user->access & 4) {
// ...
}
// By yerda qanday mantiq ketayorgani vaqti kelib o'zingizni ham yodingizdan ko'tariladi
$user->access ^= 2;
Yaxshi:
declare(strict_types=1);
class User
{
public const ACCESS_READ = 1;
public const ACCESS_CREATE = 2;
public const ACCESS_UPDATE = 4;
public const ACCESS_DELETE = 8;
// Bu yerda esa hammasi tushunarli. Boshlang'ich holatda `$access`ga o'qish, tahrirlash va yaratish huquqlari o'zlashtirilmoqda.
public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE;
}
if ($user->access & User::ACCESS_UPDATE) {
// tahrirlash ...
}
// Nimadir yaratishga bo'lgan huquqni cheklash
$user->access ^= User::ACCESS_CREATE;
Yomon:
declare(strict_types=1);
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches[1], $matches[2]);
Yomon emas:
Bu yaxshiroq, lekin biz haliham to'liq mantiqni tushunish uchun regex'ga bog'liqmiz.
declare(strict_types=1);
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);
[, $city, $zipCode] = $matches;
saveCityZipCode($city, $zipCode);
Yaxshi:
Subpattern'larni nomlash orqali regex'ga bog'liqligimizni kamaytirdik.
declare(strict_types=1);
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches['city'], $matches['zipCode']);
Shart operatorlarini bir birini ichiga chuqur joylashtirish va qiymatlarni erta qaytarishni oldini oling (1-qism)
Juda ham ko'p if-else operatorlari kodingizni tushunishni qiyinlashtiradi. Aniqlik mujmallikdan ko'ra yaxshiroq.
Yomon:
declare(strict_types=1);
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;
}
Yaxshi:
declare(strict_types=1);
function isShopOpen(string $day): bool
{
if (empty($day)) {
return false;
}
$openingDays = ['friday', 'saturday', 'sunday'];
return in_array(strtolower($day), $openingDays, true);
}
Shart operatorlarini bir birini ichiga chuqur joylashtirish va qiymatlarni erta qaytarishni oldini oling (2-qism)
Yomon:
declare(strict_types=1);
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';
}
Yaxshi:
declare(strict_types=1);
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);
}
Kodingiz o'quvchisini o'zgaruvchi nimani anglatishini tarjima qilishiga majburlamang. Aniqlik mujmallikdan ko'ra yaxshiroq.
Yomon:
$l = ['Austin', 'New York', 'San Francisco'];
for ($i = 0; $i < count($l); $i++) {
$li = $l[$i];
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Shoshmang, `$li` nima bo'ldi bu yana?
dispatch($li);
}
Yaxshi:
declare(strict_types=1);
$locations = ['Austin', 'New York', 'San Francisco'];
foreach ($locations as $location) {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch($location);
}
Agar class/obyekt'ingizni nomi nimanidir anglatib turgan bo'lsa, o'zgaruvchingizni nomida uni takrorlamang
Yomon:
declare(strict_types=1);
class Car
{
public $carMake;
public $carModel;
public $carColor;
//...
}
Yaxshi:
declare(strict_types=1);
class Car
{
public $make;
public $model;
public $color;
//...
}
Yaxshi emas:
Bu yaxshi emas chunki $breweryName
ning qiymati NULL
bo'lishi mumkin.
function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void
{
// ...
}
Yomon emas:
Bu variant oldingisiga qaraganda ancha tushunarli, ammo u o'zgaruvchining qiymatini yaxshiroq boshqaradi.
function createMicrobrewery($name = null): void
{
$breweryName = $name ?: 'Hipster Brew Co.';
// ...
}
Yaxshi:
Toifalarni boshqarish dan
foydalanishingiz mumkin va $breweryName
ning qiymati NULL
ga teng bo'lmasligiga ishonchingiz komil bo'ladi.
function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
// ...
}
Toifa bilan taqqoslashdan foydalaning
Yaxshi emas:
Oddiy taqqoslash satrni butun songa aylantiradi.
declare(strict_types=1);
$a = '42';
$b = 42;
if ($a != $b) {
// Ifoda bajarilmaydi
}
$a != $b
ifodasi FALSE
(yolg'on) qiymat qaytaradi, aslide esa TRUE
(rost) qaytarishi kerak. Chunki, satr
toifasidagi 42
butun toifadagi 42
dan farq qiladi.
Yaxshi:
Toifa bilan taqqoslaganda o'zgaruvchilarning qiymati bilan birgalikda toifasi ham solishtiriladi.
declare(strict_types=1);
$a = '42';
$b = 42;
if ($a !== $b) {
// Ifoda bajariladi
}
$a !== $b
taqqosi TRUE
(rost) qiymat qaytaradi.
Null birlashma operatori PHP 7 versiyadan boshlab joriy
qilingan yangi operator hisoblanadi. Bu operator ternar isset ()
bilan birgalikda ishlatish kerak bo'lgan oddiy holat
uchun sintaktik qisqartma sifatida qo'shilgan. Bu operator dastlabki operanda mavjud bo'lsa va u null
ga teng bo'lmasa
shu operandani qaytaradi, aks holda keyingi operandani qaytaradi;
Yomon:
declare(strict_types=1);
if (isset($_GET['name'])) {
$name = $_GET['name'];
} elseif (isset($_POST['name'])) {
$name = $_POST['name'];
} else {
$name = 'nobody';
}
Yaxshi:
declare(strict_types=1);
$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody';
Funksiya qabul qiluvchi parametrlarini cheklash juda muhimdir, chunki bu testlash jarayonini osonlashtiradi. Uchtadan ortiq parametrlarni qabul qilish har bir holatni testlayotgan paytingizda ko'plab qiyinchiliklarni yuzaga keltiradi.
Argumentsiz funksiyalar bu ideal holatdir. Bir yoki ikkita argument qabul qilish yaxshi, uchtadan esa qochgan ma'qul. Bundan boshqa holatlarda esa imkon qadar birlashtirish kerak. Odatda, agar sizda 2 tadan ortiq argument bo'lsa u holda sizning funksiyangiz juda ko'p amalni bajarishga harakat qiladi. Bunday bo'lmagan hollarda esa argumentlarni obyektlarga joylashtiring.
Yomon:
declare(strict_types=1);
class Questionnaire
{
public function __construct(
string $firstname,
string $lastname,
string $patronymic,
string $region,
string $district,
string $city,
string $phone,
string $email
) {
// ...
}
}
Yaxshi:
declare(strict_types=1);
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)
{
// ...
}
}
Yomon:
class Email
{
//...
public function handle(): void
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
// Nima bu ? Hozir biz faylga yozayapmizmi ?
$message->handle();
Yaxshi:
class Email
{
//...
public function send(): void
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
// Tushunarli va aniq
$message->send();
Sizda bir darajadan ortiq abstraktsiya bo'lganida funksiyangiz odatada ko'p ishlaydi. Funksiyalarni ajratish qayta foydalanuvchanlikni yaxshilaydi va testlashni osonlashtiradi.
Yomon:
declare(strict_types=1);
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...
}
}
Yaxshi emas:
Biz biroz funksionnalikni amalga oshirdik, lekin parseBetterPHPAlternative()
funksiyasi haliham testlab bo'lmaydigan
darajada juda murakkab.
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...
}
}
Yaxshi:
Eng yaxshi yechim bu parseBetterPHPAlternative()
funksiyasini bog'liqligini ko'chirish.
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...
}
}
}
Mantiqiy operatorlar foydalanuvchiga funksiya bir nechta amal bajarishini bildiradi. Funksiya bitta amalni bajraishi kerak. Agar funksiya mantiqiy asosga ko'ra turli xil amllarni bajarishi kerak bo'lsa, uni ajrating.
Yomon:
declare(strict_types=1);
function createFile(string $name, bool $temp = false): void
{
if ($temp) {
touch('./temp/' . $name);
} else {
touch($name);
}
}
Yaxshi:
declare(strict_types=1);
function createFile(string $name): void
{
touch($name);
}
function createTempFile(string $name): void
{
touch('./temp/' . $name);
}
Agar funksiya qabul qilgan qiymatidan tashqari amal bajarsa va boshqa bir qiymat yoki qiymatlarni qaytarsa, yon ta'sirni yuzaga keltiradi. Yon ta'sirlar faylga yozish, ba'zi global o'zgaruvchilarni o'zgartirish yoki tasodifan barcha pullaringizni begonaga o'takib yuborish kabilar bo'lishi mumkin.
Masalan, ba'zida dasturda yon ta'sirlarga ega bo'lishingizga to'g'ri kelib qoladi. Yuqorida aytilganidek, faylga yozish kerak deylik. Siz qilmoqchi bo'lgan narsa, buni qilayotgan joyingizni markazlashtirishdir. Muayyan faylga yozadigan bir nechta funksiya va sinflar yaratmang. Buni amalga oshiradigan bitta servis yarating. Bitta va yagona.
Asosiysi, hech qanday strukturaga ega bo'lmagan ob'ektlar o'rtasida ma'lumot almashinish, har qanday yozilishi mumkin bo'lgan dinamik ma'lumot turlaridan foydalanish va yon ta'sirlar paydo bo'ladigan joyni markazlashtirmaslik kabi umumiy tuzoqlardan qochishdir. Agar siz buni amalga oshira olsangiz, boshqa dasturchilarning katta qismidan ko'ra baxtliroq bo' lasiz.
Yomon:
declare(strict_types=1);
// Global oʻzgaruvchiga quyidagi funksiya orqali havola qilingan.
// Agar bizda aynan shu nomdan foydalanadigan boshqa funksiya bo'lganida, endi u massiv bo'lardi va uni buzishi mumkin edi.
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName(): void
{
global $name;
$name = explode(' ', $name);
}
splitIntoFirstAndLastName();
var_dump($name);
// ['Ryan', 'McDermott'];
Yaxshi:
declare(strict_types=1);
function splitIntoFirstAndLastName(string $name): array
{
return explode(' ', $name);
}
$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);
var_dump($name);
// 'Ryan McDermott';
var_dump($newName);
// ['Ryan', 'McDermott'];
Global funksiyalarga o'zgartirish kiritish ko'p tillarda yomon amaliyotdir, chunki siz boshqa kutubxona bilan to'qnash
kelishingiz mumkin va sizning API foydalanuvchingiz amaliyotga tadbiq etilganda istisnoga duch kelmaguncha muammoli bo'
lib qolaveradi.
Aytaylik, quyidagi misolni ko'rib chiqamiz: agar siz konfiguratsiyani massiv ko'rinishida qabul qilishingizga to'g'ri
kelib qolsa nima bo'ladi? Siz config()
ga o'xshash global funktsiya yozishingiz mumkin, lekin u xuddi shu vazifani
bajaruvchi boshqa kutubxona bilan muammo yuzaga kelishi mumkin.
Yomon:
declare(strict_types=1);
function config(): array
{
return [
'foo' => 'bar',
];
}
Yaxshi:
declare(strict_types=1);
class Configuration
{
private $configuration = [];
public function __construct(array $configuration)
{
$this->configuration = $configuration;
}
public function get(string $key): ?string
{
// null birlashma operatori
return $this->configuration[$key] ?? null;
}
}
Konfiguratsiyalarni chaqirib oling va Configuration
sinfidan vorislik oling.
declare(strict_types=1);
$configuration = new Configuration([
'foo' => 'bar',
]);
Endi esa dasturingizda Configuration
vorisidan foydalanishingiz kerak.
Singleton anti-pattern hisoblanadi. Brian Button shunday deydi:
- Ular odatda global instance (global namuna) sifatida ishlatiladi, nega bu yomon? Chunki siz dasturingiz kodidagi bog'liqliklarni interfeyslar orqali aniq ko'rsatish o'rniga bog'liqliklarni yashirasiz. Biror narsani tarqatmaslik uchun uni gloabl qilish code smell (koddagi muammoni ko'rsatishi mumkin bo'lgan umumiy dasturlash xususiyati).
- Ular o'zlarining yaratilishlari va hayot siklini nazorat qilishlari tufayli yagona javobgarlik prinspini buzadi.
- Ular tabiatan kodni mahkam bog'lanishiga olib keladi. Bu esa ko'p hollarda testlash jarayonini qiyinlashtiradi.
- Ular dastur ishlashining boshidan oxirigacha ma'lumotlarni olib yuradilar. Bu esa testlashni qiyinlashtiruvchi yana bir faktor, unit testlashni bajarishda muammoga sabab bo'lishi mumkin. Nega? Chunki har bir unit testlash bir biridan mustaqil bo'lgani yaxshi.
Bu yerda muammoning ildizi haqida Misko Heveryning fikrlarini keltirib o'tilgan.
Yomon:
declare(strict_types=1);
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();
Yaxshi:
declare(strict_types=1);
class DBConnection
{
public function __construct(string $dsn)
{
// ...
}
// ...
}
Create instance of DBConnection
class and configure it
with DSN.
declare(strict_types=1);
$connection = new DBConnection($dsn);
Va endi siz dasturingizda DBConnection
obyektidan foydalanishingiz kerak.
Yomon:
declare(strict_types=1);
if ($article->state === 'published') {
// ...
}
Yaxshi:
declare(strict_types=1);
if ($article->isPublished()) {
// ...
}
Yomon:
declare(strict_types=1);
function isDOMNodeNotPresent(DOMNode $node): bool
{
// ...
}
if (! isDOMNodeNotPresent($node)) {
// ...
}
Yaxshi:
declare(strict_types=1);
function isDOMNodePresent(DOMNode $node): bool
{
// ...
}
if (isDOMNodePresent($node)) {
// ...
}
Bu uddalab bo'lmas vazifadek ko'rinadi. Buni birinchi marta eshitganda, ko'pchilik aytadiki "if
siz men buni qanday qila olishim mumkin?" Buning javobi shundaki, siz ko'p hollarda polimarfizmdan foydalangan holda bunga erishishingiz mumkin. Ikkinchi savol shundaki, "Yaxshi, bu zo'r lekin lekin nega men buni qilishim kerak?". Javob shuki, biz o'rganayotgan toza kod konseptsiyasiga ko'ra funksiya faqat birgina amalni bajarishi lozim. Qachonki dasturingizdagi funksiya yoki sinfingizda shart operatori qatnashsa, siz foydalanuvchiga funksiyangiz bir dan ortiq amalni bajarayotganini aytasiz. Eslab qoling, faqatgina bir narsani qiling.
Yomon:
declare(strict_types=1);
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();
}
}
}
Yaxshi:
declare(strict_types=1);
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();
}
}
PHP dinamik tipli dasturlash tili hisoblanadi, ya'ni funksiya har qanday turdagi argumentlarni qabul qilishi mumkin. Ba'zida bunday erkinlik noqulaylik yaratadi va funksiyada argumant turini tekshirgingiz kelib qoladi. Buni qilishdan qochishning ko'plab usullari mavjud. Ko'rib chiqilishi kerak bo'lgan birinchi holat - bu ishonchli API hisoblanadi.
Yomon:
declare(strict_types=1);
function travelToTexas($vehicle): void
{
if ($vehicle instanceof Bicycle) {
$vehicle->pedalTo(new Location('texas'));
} elseif ($vehicle instanceof Car) {
$vehicle->driveTo(new Location('texas'));
}
}
Yaxshi:
declare(strict_types=1);
function travelToTexas(Vehicle $vehicle): void
{
$vehicle->travelTo(new Location('texas'));
}
Shuni inobatga olingki agar siz satrlar, butun sonlar va massivlar kabi asosiy primitiv qiymatlar bilan ishlayotgan bo'lsangiz va PHP 7+ versiyadan foydalansangiz shunigdek polimorfizmdan foydalana olmasangiz, lekin shundaham o'zgaruvchi tipini tekshirish kerak bo'lsa, tiplarni e'lon qilish yoki strict_types rejimidan foydalanishingiz mumkin. Bu kalit so'z orqali kodda standart PHP sintaksisi ustiga statik tipizatsiya qilish ta'minlanadi. Tipni tekshirishga majburlash bilan bog'liq muammo shundaki, uni amalga oshirish shunchalik qo'shimcha so'zlarni talab qiladiki, siz olgan soxta "turli xavfsizlik" yo'qolgan o'qish qobiliyatini qoplamaydi. Kodni toza yozing, yaxshi testlar yozing va yaxshi kod sharhlariga ega bo'ling. Aks holda, bularning barchasini statik turdagi e'lon qilish yoki strict_types rejimi bilan bajaring.
Yomon:
declare(strict_types=1);
function combine($val1, $val2): int
{
if (! is_numeric($val1) || ! is_numeric($val2)) {
throw new Exception('Must be of type Number');
}
return $val1 + $val2;
}
Yaxshi:
declare(strict_types=1);
function combine(int $val1, int $val2): int
{
return $val1 + $val2;
}
O‘lik kod dublikat kod kabi yomondir. Bunday turdagi kodni dasturingizda saqlab turishingizga sabab yo‘q. Agar u dasturda ishlatilmasa undan qutuling! Agar keyinchalik kerak bo‘lib qolsa, u git tarixida saqlanib qoladi.
Yomon:
declare(strict_types=1);
function oldRequestModule(string $url): void
{
// ...
}
function newRequestModule(string $url): void
{
// ...
}
$request = newRequestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');
Yaxshi:
declare(strict_types=1);
function requestModule(string $url): void
{
// ...
}
$request = requestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');
PHPda metodlar uchun public
, protected
va private
kabi kalit so'zlardan foydalaniladi. Bulardan foydalanish orqali obyektning xususiyatlarini o'zgartirishni boshqarish mumkin.
- Obyekt xususiyatini olishdan tashqari ko'proq narsa qilishni xohlasangiz, dasturingizdagi har bir qismni qidirish yoki o'zgartirish shart emas.
set
qilayotganda validatsiya qo'shishni osonlashtiradi.- Ichkaridagi amalga oshiriladigan jarayonni inkapsulatsiyalaydi.
- Qiymat olishda va o'rnatishda, loglashni qo'shish va xatoliklarni tutib olish oson.
- Sinfdan meros olish orqali boshlang'ich funksiyalarni qayta yoza olasiz.
- Obyekt xususiyatlarini lazy load qila olasiz, masalan serverdan olganda.
Shuningdek, bu Open/Closed tamoyilining bir qismi hisoblanadi.
Yomon:
declare(strict_types=1);
class BankAccount
{
public $balance = 1000;
}
$bankAccount = new BankAccount();
// Buy shoes...
$bankAccount->balance -= 100;
Yaxshi:
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
metodlar va xususiyatlar o'zgarishlar uchun juda xavfli hisoblanadi, chunki ba'zi tashqi kodlar ularga tayanishi mumkin va siz aynan qaysi kod ularga tayanayotganini nazorat qila olmaysiz. Sinfdagi o'zgarishlar sinfning barcha foydalanuvchilari uchun xavfli hisoblanadi. -
protected
modifikatorihampublic
kabi xavfli hisoblanadi, chunki ular har qanday voris sinf uchun ochiq. Buni shuni anglatadiki, public va protected modifikatorlar orasidagi farq faqatgina kirish mexanizmida, lekin inkapsulatsiya bu bir xilda xavflidir. Sinfdagi o'zgarishlar sinfning barcha vorislari uchun xavfli hisoblanadi. -
private
modifikatori esa faqat yagona sinf chegarasida o'zgartirish xavfliligini kafolatlaydi (kodingiz o'zgartirishlar uchun xavfsiz va siz Jenga effectiga duch kelmaysiz).
Shuning uchun, private
modifikatordan foydalanishdan boshlang, qo'shimcha sinflarda foydalanish kerak bo'lib qolsa public/protected
modifikatorlari orqali bunga erish olasiz.
Mavzu doirasida to'liqroq ma'lumot uchun Fabien Potencier tomonidan yozilgan ushbu postni o'qishingiz mumkin.
Yomon:
declare(strict_types=1);
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;
Yaxshi:
declare(strict_types=1);
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();
Gang of Four tomonidan yozilgan Design Patterns kitobiga ko'ra, imkoni bo'lsa meros olishdan ko'ra kompozitsiyani afzal ko'rishingiz lozim. Kompozitsiya yoki meros olishga oid juda ko'p sabablar mavjud. Asosiy farq shundaki, agar aqlingiz instinktiv ravishda meros olishni tanlasa, kompozitsiyalash muammoingizni yaxshiroq modellashtirishi mumkinligi haqida o'ylang. Ba'zi hollarda bu to'g'ri yechim bo'ladi.
Ba'zan esa siz "qachon meros olishdan foydalanishim kerak?" deb o'ylarsiz. Bu sizning muammoingizga bog'liq. Quyida esa meros olish kompozitsiyalashdan ko'ra ko'proq tavsiya etiladigan hollar keltirilgan:
- Meros olish "has-a" emas balki, "is-a" munosabatni ifodalasa (Human->Animal vs. User->UserDetails).
- Asosiy sinfdagi kodni qayat ishlatish imkoniyati mavjud bo'lsa (insonlar barcha hayvonlar kabi harakatlana olishi mumkin).
- Asosiy sinfni o'zgartirish orqali meros olingan sinflarga global o'zgartirish kiritish kerak bo'lsa (barcha hayvonlar harakatlanayotganda ularning kaloriya sarfini o'zgartirish kerak bo'lsa).
Yomon:
declare(strict_types=1);
class Employee
{
private $name;
private $email;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
// ...
}
// Bu yomon chunki, Employees (xodimlar) soliq ma'lumotlariga ega
// EmployeeTaxData (soliq ma'lumotlari) Employee turiga tegishli
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;
}
// ...
}
Yaxshi:
declare(strict_types=1);
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 interfeys bu obyektga yo'naltirilgan API bo'lib metod zanjiri yordamida dastur kodining o'qilishini yaxshilashga hizmat qiladi.
Shunday vaziyatlar bo'lishi mumkinki, quruvchi obyektlarda bu shablon dastur kodining aniqligini tushiradi (masalan PHPUnit Mock Builder yoki Doctrine Query Builder), ko'pincha bunday holat ba'zi kamchiliklarga olib keladi:
- Inkapsulatsiyani buzish.
- Dekorator shablonini buzish.
- mock ma'lumotlarni testlashni qiyinlashtiradi.
- Kommitlardagi farqlarni o'qishni qiyinlashtiradi.
To'liq ma'lumot uchun mavzuga oid Marco Pivetta tomonidan yozilgan ushbu blogni o'qishingiz mumkin.
Yomon:
declare(strict_types=1);
class Car
{
private $make = 'Honda';
private $model = 'Accord';
private $color = 'white';
public function setMake(string $make): self
{
$this->make = $make;
// ESLATMA: Zanjir uchun qaytarilgan
return $this;
}
public function setModel(string $model): self
{
$this->model = $model;
// ESLATMA: Zanjir uchun qaytarilgan
return $this;
}
public function setColor(string $color): self
{
$this->color = $color;
// ESLATMA: Zanjir uchun qaytarilgan
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();
Yaxshi:
declare(strict_types=1);
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();
Imkon qadar final
sinflardan foydalaning:
- Bu orqali nazorat qilib bo'lmaydigan meros zanjirini oldi olinadi.
- Bu kompozitsiyalashni rag'batlantiradi.
- Bu yagona javobgarlik prinspiga mos keladi.
- Bu orqali dasturchilarni himoyalangan metodlarga kirish uchun sinfni kengaytirish o'rniga umumiy metodlardan foydalanishga undash mumkin.
- Bu sizga sinfingizdan foydalanadigan ilovalarning hech qanday uzilishisiz kodingizni o'zgartirish o'zgartirish imkonini beradi.
Yagona shart shundaki sinfingiz interfeysdan foydalanishi va boshqa umumiy metodlar bo'lmasligi lozim.
To'liqroq ma'lumot uchun Marco Pivetta (Ocramius) tomonidan yozilgan ushbu postni o'qishingiz mumkin.
Yomon:
declare(strict_types=1);
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;
}
}
Yaxshi:
declare(strict_types=1);
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 - bu Robert Martin tomonidan nomlangan birinchi besh tamoyil uchun Michael Feathers kiritgan mnemonik qisqartma bo'lib, u obyektga yo'naltirilgan dasturlash va dizaynning beshta asosiy tamoyilini anglatadi.
- S: Yagona javobgarlik printspi (Single Responsibility Principle, SRP)
- O: Ochiq/Yopiqlik printspi (Open/Closed Principle, OCP)
- L: Liskov almashtirish printsipi (Liskov Substitution Principle, LSP)
- I: Interfeysni ajratish printsipi (Interface Segregation Principle, ISP)
- D: Bog'liqlik inversiyasi printsipi (Dependency Inversion Principle, DIP)
"Clean code"da aytilganidek, "Sinfni o'zgartirish uchun hech qachon bir nechta sabab bo'lmasligi kerak". Muammo shundaki, sizning sinfingiz kontseptual jihatdan birlashtirilgan bo'lmaydi va bu uni o'zgartirish uchun ko'p sabablarni yuzaga keltiradi. Sinfni o'zgartirish uchun ketadigan vaqtni kamaytirish muhimdir. Bu juda muhim, chunki bitta sinfda juda ko'p funksiya mavjud bo'lsa va siz uning bir qismini o'zgartirsangiz, bu o'zgartirish dasturdagi boshqa bog'liq modullarga qanday ta'sir qilishini tushunish qiyin bo'lishi mumkin.
Yomon:
declare(strict_types=1);
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
{
// ...
}
}
Yaxshi:
declare(strict_types=1);
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()) {
// ...
}
}
}
Bertrrand Meyerga ko'ra, "dasturiy qismlar (sinflar, modullar, funktsiyalar va boshqalar) kengaytirish uchun ochiq ammo o'zgartirish uchun yopiq bo'lishi kerak." Bu nimani anglatadi? Ushbu tamoyilga ko'ra siz foydalanuvchilarga mavjud kodni o'zgartirmasdan yangi funktsiyalarni qo'shishga imkon berishingiz kerak.
Yomon:
declare(strict_types=1);
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
}
}
Yaxshi:
declare(strict_types=1);
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);
}
}
Bu juda oddiy tushuncha uchun murakkab atama. Rasmiy ravishda "Agar S ning turi T bo'lsa, u holda T tipidagi obyektlar S tipidagi obyektlar bilan dasturning istalgan xususiyatlarini o'zgartirmasdan almashtirilishi mumkin (ya'ni, S tipidagi obyektlar T tipidagi obyektlarni almashtirishi mumkin)." Bu yanada murakkabroq ta'rif.
Soddaroq qilib aytganda, agar sizda ota sinfi va bola sinfi bo'lsa, unda asosiy sinf va bola sinfi noto'g'ri natijalarga erishmasdan bir-birining o'rnida ishlatilishi mumkin. Bu hali ham chalkash bo'lishi mumkin, shuning uchun klassik kvadrat-to'rtburchak misolini ko'rib chiqaylik. Matematik jihatdan kvadrat to'rtburchakdir, lekin agar siz uni meros orqali "is-a" munosabatidan foydalanib modellashtirsangiz, tezda muammoga duch kelasiz.
Yomon:
declare(strict_types=1);
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);
// YOMON: Kvadrat uchun 25 ni qaytaradi. Aslida esa 20 bo'lishi kerak.
echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()) . PHP_EOL;
}
$rectangles = [new Rectangle(), new Square()];
foreach ($rectangles as $rectangle) {
printArea($rectangle);
}
Yaxshi:
Eng yaxshi usul - to'rtburchaklarni ajratish va ikkala shakl uchun ham umumiy kichik turni ajratib olishdir.
Kvadrat va to'rtburchakning aniq bo'lgan o'xshashligiga qaramay, ular bir-biridan farq qiladi. Kvadrat romb va parallelogramm bilan to'rtburchaklar bilan juda ko'p umumiyliklarga ega, ammo ular kichik tur emas. Kvadrat, to'rtburchak, romb va parallelogramma o'xshash bo'lsa-da, o'ziga xos xususiyatlarga ega bo'lgan alohida shakllardir.
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" ga ko'ra, "Mijozlarni o'zlari foydalanmayotgan interfeyslarga bog'lanishga majburlamaslik kerak".
Ko'plab sozlamali obyektlarni talab qiladigan sinflar uchun ushbu printsip yaxshi misol bo'la oladi. Mijozlardan katta hajmdagi variantlarni o'rnatishni talab qilmaslik foydalidir, chunki ko'pincha ularga barcha sozlamalar kerak bo'lmaydi. Ularni ixtiyoriy qilish ortiqcha interfeysga ega bo'lishni oldini oladi.
Yomon:
declare(strict_types=1);
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
}
}
Yaxshi:
Har bir ishchi xodim emas, lekin har bir xodim ishchi hisoblanadi.
declare(strict_types=1);
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
}
}
Ushbu tamoyil ikkita muhim narsani belgilaydi:
- Yuqori darajadagi modullar past darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.
- Abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.
Avvaliga buni tushunish qiyin bo'lishi mumkin, lekin agar siz PHP freymvorklari (masalan, Symfony) bilan ishlagan bo'lsangiz, bu tamoyilning Dependency Injection (DI) ko'rinishida amalga oshirilishini ko'rgansiz. Ular bir xil tushunchalar bo'lmasada, DIP yuqori darajadagi modullarni o'zining past darajadagi modullari tafsilotlarini bilishni va ularni sozlashni oldini oladi. Buni DI orqali amalga oshirishi mumkin. Buning asosiy afzalligi shundaki, u modullar orasidagi bog'lanishni kamaytiradi. Bog'lanishlar orqali kod yozish yaxshi emas, chunki u kodingizni o'zgartirishni qiyinlashtiradi.
Yomon:
declare(strict_types=1);
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();
}
}
Yaxshi:
declare(strict_types=1);
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 tamoyiliga amal qilishga harakat qiling.
Yozgan kodingizni takrorlamaslikka imkon qadar harakat qiling. Takrorlangan kod bu yomon, chunki siz biror qismda mantiqni o'zgartirsangiz, boshqa bir nechta joyda ham o'zgartirishlar qilishingizga to'g'ri kelib qoladi.
Tasavvur qiling, siz restoran ishlayapsiz va siz o'zingizning anjomlaringizni kuzatib borasiz: barcha pomidorlaringiz, piyozlaringiz, sarimsoqlaringiz, ziravorlaringiz va boshqalar. Agar sizda buni saqlaydigan bir nechta ro'yxatlar bo'lsa, pomidor bilan taom berishda hammasi yangilanishi kerak bo'ladi. Agar sizda faqat bitta ro'yxat bo'lsa, yangilash uchun faqat bitta qismni o'zgartirasiz!
Ko'pincha takrorlangan kod mavjud bo'ladi, chunki sizda ikkita yoki undan ko'p bir oz farq qiladigan amallar mavjud, ular juda ko'p umumiylikga ega, ammo ularning farqlari sizni bir xil amallarni bajaradigan ikki yoki undan ortiq alohida funktsiyalarga ega bo'lishga majbur qiladi. Ikki nusxadagi kodni olib tashlash bu turli xil amallar to'plamini faqat bitta funktsiya/modul/sinf bilan boshqara oladigan abstraksiya yaratishni anglatadi.
Abstraktsiyani to'g'ri amalga oshirish juda muhim, buning uchun siz sinflar bo'limida keltirilgan SOLID tamoyillariga amal qilishingiz kerak. Noto'g'ri abstraktsiyalar takrorlangan koddan ham yomonroq bo'lishi mumkin, shuning uchun ehtiyot bo'ling! Agar to'g'ri abstraksiya qila olsangiz, buni amalga oshiring! O'zingizni takrorlamang, aks holda siz bir amalni o'zgartirmoqchi bo'lganingizda bir nechta joylarni yangilashingizga to'g'ri keladi.
Yomon:
declare(strict_types=1);
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);
}
}
Yaxshi:
declare(strict_types=1);
function showList(array $employees): void
{
foreach ($employees as $employee) {
$expectedSalary = $employee->calculateExpectedSalary();
$experience = $employee->getExperience();
$githubLink = $employee->getGithubLink();
$data = [$expectedSalary, $experience, $githubLink];
render($data);
}
}
Very good:
Kodning ixcham versiyasidan foydalanish yanayam yaxshiroq.
declare(strict_types=1);
function showList(array $employees): void
{
foreach ($employees as $employee) {
render([$employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink()]);
}
}
Boshqa tillarda:
- 🇨🇳 Xitoy:
- 🇷🇺 Rus:
- 🇪🇸 Ispan:
- 🇧🇷 Portugal:
- 🇹🇭 Tay:
- 🇫🇷 Fransuz:
- 🇻🇳 Vetnam
- 🇰🇷 Koreys:
- 🇹🇷 Turk:
- 🇮🇷 Eron:
- 🇧🇩 Bangla:
- 🇪🇬 Arab:
- 🇯🇵 Yapon:
- :uz: O'zbek: