سوالاتی که یک برنامه نویش ارشد php باید بتواند به آنها پاسخ دهد
برای پیشرفت در مسیر شغلی بهعنوان یک برنامهنویس ارشد PHP، فراتر از نوشتن کد ساده، باید اصول معماری، الگوهای طراحی، امکانات جدید زبان و شیوههای بهینه را بشناسید. در این مقاله ۱۰ سوال کلیدی در سطح پیشرفته گردآوری شده که هم دید شما را نسبت به امکانات زبان گسترش میدهد و هم با مثالهای عملی، نحوه پیادهسازی و بهترین شیوهها را تشریح میکند.
سوالات
سوال 1 :
مفهوم Late Static Binding در PHP چیست؟ تفاوت آن با binding معمولی (early binding) چیست؟ یک مثال کد ساده هم بنویسید.
سوال 2 :
فرق abstract class و interface در PHP چیست و در چه مواقعی از هرکدام استفاده میکنید؟
سوال 3
تفاوت خطا (Error) و استثنا (Exception) در PHP چیست و چگونه یک سیستم مدیریت (handling) سفارشی برای خطاها و استثناها پیادهسازی میکنید؟ لطفاً مراحل و توابع کلیدی را هم نام ببرید.
سوال 4
PSR-4 Autoloading چیست و چگونه آن را در یک پروژه PHP با Composer تنظیم میکنید؟
سوال 5
مفهوم Traits در PHP چیست؟ نحوه استفاده از آنها و مدیریت تضاد متدها را توضیح دهید و یک مثال کد بیاورید.
سوال 6
Generators در PHP چه هستند و چگونه با yield کار میکنند؟ مزایای استفاده از آنها نسبت به بازگرداندن آرایه را توضیح دهید و یک مثال بنویسید.
سوال 7
متدهای جادویی (magic methods) در PHP چیستند؟ تفاوت __call، __callStatic، __invoke، __get و __set را توضیح دهید و یک مثال کد بنویسید.
سوال 8
اصول SOLID در برنامهنویسی شیءگرا چیستند؟ دو اصل زیر را توضیح دهید و برای هر یک یک مثال PHP بنویسید:
Single Responsibility Principle (SRP)
Open/Closed Principle (OCP)
سوال 9
Dependency Injection چیست؟ روشهای مختلف پیادهسازی آن (Constructor, Setter, Interface Injection) را توضیح دهید و یک مثال کد برای Constructor Injection بنویسید.
سوال 10
مفهوم Just-in-Time (JIT) compilation در PHP 8 چیست؟ مزایا و محدودیتهای آن را توضیح دهید.
پاسخ سوالات
در ادامه پاسخ تمام سوال ها به صورت تشریحی و نمونه کد آورده شده است ، قبل از مشاهده آنها سعی کنید خودتان به سوالات پاسخ دهید و پس از مشاهده جواب سوالات آنها را تمرین کنید ، اگر نیاز به سفارش طراحی پلاگین وردپرس دارید با ما تماس بگیرید.
پاسخ سوال ۱
مفهوم Late Static Binding در PHP به این معناست که ارجاع به متدها یا پراپرتیهای static در کلاسهای ارثبرده، به کلاس فرزند اشاره میکند و نه کلاس پایه. این موضوع با استفاده از کلمهکلیدی static::
پیادهسازی میشود.
تفاوت با Early Binding:
- در Early Binding (کلمهکلیدی
self::
)، ارثبری نادیده گرفته میشود و همیشه به تعریف کلاس جاری (محل تعریف متد) اشاره میکند. - در Late Static Binding (
static::
)، اگر فراخوانی از کلاس فرزند صورت گیرد، متدهای بازتعریفشده در آن کلاس اجرا میشوند.
مثال کد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php class A { public static function who() { echo __CLASS__; } public static function test() { // با self::who() همیشه "A" چاپ میشود // با static::who() اگر از B صدا زده شود، "B" چاپ میشود static::who(); } } class B extends A { public static function who() { echo __CLASS__; } } // چاپ B به جای A بهخاطر Late Static Binding B::test(); |
پاسخ سوال ۲
تفاوت abstract class و interface:
- تعریف
- abstract class میتواند شامل متدهای با پیادهسازی (concrete) و بدون پیادهسازی (abstract) باشد.
- interface فقط میتواند متدهای بدون پیادهسازی (abstract) داشته باشد و از PHP 8 میتواند متدهای با پیادهسازی پیشفرض (default) نیز شامل شود.
- ارثبری
- یک کلاس میتواند فقط از یک abstract class ارث ببرد.
- یک کلاس میتواند همزمان چندین interface را پیادهسازی کند.
- قابلیت نگهداری وضعیت (State)
- abstract class میتواند پراپرتی (state) تعریف کند.
- interface تا قبل از PHP 8 نمیتوانست پراپرتی داشته باشد؛ از PHP 8 فقط میتوان متدهای ثابت (static) و const تعریف کرد.
- موارد استفاده
- وقتی بخواهید یک سلسلهمراتب کلاسها با پیادهسازی مشترک داشته باشید و برخی متدها واجب (abstract) باشند، از abstract class استفاده میکنید.
- وقتی صرفاً یک قرارداد (contract) بدون جزئیات پیادهسازی میخواهید تعریف کنید و کلاسها الزاماً باید متدها را خودشان پیاده کنند، از interface استفاده میکنید.
مثال کد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php abstract class Logger { protected string $path; public function __construct(string $path) { $this->path = $path; } abstract public function log(string $message): void; public function write(string $message): void { file_put_contents($this->path, $message . PHP_EOL, FILE_APPEND); } } interface Formatter { public function format(string $message): string; } class FileLogger extends Logger implements Formatter { public function log(string $message): void { $this->write($this->format($message)); } public function format(string $message): string { return '[' . date('Y-m-d H:i:s') . '] ' . $message; } } </pre |
پاسخ سوال ۳
تفاوت Error و Exception در PHP و مدیریت سفارشی:
-
- تفاوت کلی
-
- Error (مثلاً ParseError، TypeError) معمولاً از نوع خطاهای سطح پایین و غیرقابل پیشبینی است و پیش از PHP 7 قابل مدیریت نیست.
-
- Exception (هر کلاس مشتق از \Exception) برای مواردی است که برنامهنویس انتظار آن را دارد و میتواند آنها را try/catch کند.
-
- تفاوت کلی
-
- پیادهسازی مدیریت سفارشی
1.تعریف تابع هندلر برای Exception
1 2 3 4 5 6 |
function exception_handler(Throwable $e): void { error_log("Exception: " . $e->getMessage()); http_response_code(500); echo "Internal Server Error."; } set_exception_handler('exception_handler'); |
2.تعریف تابع هندلر برای Error
1 2 3 4 5 6 |
function error_handler(int $errno, string $errstr, string $errfile, int $errline): bool { error_log("Error [$errno] $errstr in $errfile on line $errline"); // تبدیل Error به Exception برای统一 مدیریت throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } set_error_handler('error_handler'); |
3.نهایی برای قطع و تمیز کردن
1 2 3 4 5 6 7 8 9 |
register_shutdown_function(function() { $error = error_get_last(); if ($error !== null) { // مدیریت fatal error exception_handler(new ErrorException( $error['message'], 0, $error['type'], $error['file'], $error['line'] )); } }); |
پاسخ سوال ۴
PSR-4 یک استاندارد بارگذاری خودکار (Autoloading) در PHP است که بر مبنای نگاشت namespaceها به ساختار دایرکتوری کار میکند. با این روش، نیازی به require
یا include
دستی فایلها نیست و صرفاً با تعریف قاعدهای در فایل composer.json
کلاسها در زمان اجرا بارگذاری میشوند.
برای پیکربندی در Composer کافی است بخشی به نام autoload
اضافه کنید. بهعنوان مثال اگر کدهای شما در پوشهی src/
قرار دارند و فضای نام اصلی شما App\
است، بخش مربوطه به این شکل خواهد بود:
1 2 3 4 5 6 7 |
{ "autoload": { "psr-4": { "App\\": "src/" } } } |
پس از ویرایش این فایل، دستور زیر را اجرا کنید تا فایلهای بارگذاری خودکار بازسازی شوند:
1 |
composer dump-autoload |
از این پس هر بار که در کدتان از یک کلاس با namespace App\…
استفاده کنید، Composer مسیر فایل مربوطه را بهصورت خودکار مییابد و لود میکند.
پاسخ سوال ۵
مفهوم Traits و مدیریت تضاد متدها در PHP:
Traits مکانیزمی برای اشتراک کد بین کلاسهای غیرمرتبط ارائه میدهند؛ مثل mixinها عمل میکنند. با استفاده از کلیدواژه use
میتوانید متدها و پراپرتیهای یک Trait را داخل کلاس بیاورید.
نحوه تعریف و استفاده
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php trait LoggerTrait { public function log(string $msg): void { echo "[LOG] $msg\n"; } } trait FormatterTrait { public function format(string $msg): string { return strtoupper($msg); } } class Service { use LoggerTrait, FormatterTrait; public function process(string $data): void { $this->log($this->format($data)); } } $svc = new Service(); $svc->process('hello'); // خروجی: [LOG] HELLO |
مدیریت تضاد متدها
اگر دو Trait متدی با یک نام داشته باشند، باید با insteadof و as مشخص کنید کدام متد استفاده و کدام با نام مستعار آورده شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php trait A { public function foo() { echo "A\n"; } } trait B { public function foo() { echo "B\n"; } } class MyClass { use A, B { B::foo insteadof A; // متد B::foo ارجحیت دارد A::foo as fooFromA; // متد A::foo با نام جدید در دسترس است } } $obj = new MyClass(); $obj->foo(); // خروجی: B $obj->fooFromA(); // خروجی: A |
پاسخ سوال ۶
Generators در PHP مکانیزمی برای تولید دادهها بهصورت تنبل (lazy) فراهم میکنند. بهجای بازگرداندن تمام دادهها در یک آرایه و مصرف یکباره حافظه، با استفاده از yield
میتوانید هر عنصر را تنها هنگام نیاز تولید کنید.
نحوه کار با yield
- هر بار که تابع Generator اجرا میشود و به یک
yield
میرسد، مقدار جاری بازگردانده و اجرای تابع متوقف میشود. - با بار فراخوانی مجدد Generator، اجرا از نقطه بعد از آخرین
yield
ادامه پیدا میکند.
مزایا نسبت به بازگرداندن آرایه
- مصرف حافظه بسیار کمتر، چون یک عنصر در هر لحظه تولید میشود.
- امکان تولید آرایههای بزرگ یا حتی نامتناهی بدون مشکل Out of Memory.
- خوانایی و سادگی در پیادهسازی حلقههای پیچیده تولید داده.
مثال کد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php function fibonacci(int $max): Generator { $a = 0; $b = 1; for ($i = 0; $i < $max; $i++) { yield $a; list($a, $b) = [$b, $a + $b]; } } foreach (fibonacci(10) as $number) { echo $number . ' '; } // خروجی: 0 1 1 2 3 5 8 13 21 34 |
پاسخ سوال ۷
متدهای جادویی (Magic Methods) در PHP متدهایی هستند که بهطور خودکار توسط مفسر فراخوانی میشوند تا رفتارهای خاص مثل دسترسی به پراپرتیهای ناموجود یا فراخوانی شیء مثل تابع را کنترل کنند. در ادامه تفاوت مهمترین آنها را میبینید:
- __call فراخوانی متدهای نمونه (non-static) ناموجود یا دسترسی به آنها. امضای متد:
public function __call(string $name, array $arguments)
- __callStatic فراخوانی متدهای static ناموجود. امضا:
public static function __callStatic(string $name, array $arguments)
- __invoke زمانی که شیء مثل یک تابع فراخوانی میشود. امضا:
public function __invoke(mixed ...$arguments)
- __get دسترسی به پراپرتی ناموجود یا غیرقابل دسترسی (private/protected). امضا:
public function __get(string $name)
- __set تنظیم مقدار برای پراپرتی ناموجود یا غیرقابل دسترسی. امضا:
public function __set(string $name, mixed $value)
مثال کد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php class MagicDemo { private array $data = []; public function __get(string $name) { return $this->data[$name] ?? null; } public function __set(string $name, $value) { $this->data[$name] = $value; } public function __call(string $name, array $arguments) { echo "فراخوانی متد نمونه ناموجود: {$name} با آرگومانها: " . implode(', ', $arguments) . "\n"; } public static function __callStatic(string $name, array $arguments) { echo "فراخوانی متد static ناموجود: {$name} با آرگومانها: " . implode(', ', $arguments) . "\n"; } public function __invoke($message) { echo "شیء فراخوانی شد با پیام: {$message}\n"; } } $obj = new MagicDemo(); // __set و __get $obj->foo = 'bar'; echo $obj->foo . "\n"; // خروجی: bar // __call $obj->nonExistingMethod(1, 2, 3); // __callStatic MagicDemo::staticMissing('a', 'b'); // __invoke $obj('سلام جهان'); |
پاسخ سوال ۸
Single Responsibility Principle هر کلاس باید تنها یک مسئولیت (reason to change) داشته باشد. یعنی هر کلاس تنها یک بخش از منطق برنامه را اداره کند تا قابلیت نگهداری و تست افزایش یابد.
مثال PHP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php // کلاس وظیفه لاگکردن دارد و تغییرات مربوط به لاگ به آن محدود است class FileLogger { private string $path; public function __construct(string $path) { $this->path = $path; } public function log(string $message): void { file_put_contents($this->path, $message . PHP_EOL, FILE_APPEND); } } // کلاس وظیفه پردازش سفارش را دارد و نیاز به تغییرات لاگ ندارد class OrderProcessor { private FileLogger $logger; public function __construct(FileLogger $logger) { $this->logger = $logger; } public function process(array $order): void { // منطق پردازش $this->logger->log("Processed order #{$order['id']}"); } } |
Open/Closed Principle کلاسها باید برای توسعه (extension) آماده باشند و برای تغییر (modification) بسته باشند. با استفاده از واسطها (interface) یا کلاسهای انتزاعی میتوانیم منطق را گسترش دهیم بدون آنکه کد اصلی را تغییر دهیم.
مثال PHP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php interface Discount { public function apply(float $amount): float; } class PercentageDiscount implements Discount { private float $rate; public function __construct(float $rate) { $this->rate = $rate; } public function apply(float $amount): float { return $amount * (1 - $this->rate); } } class Order { private Discount $discount; private float $total; public function __construct(float $total, Discount $discount) { $this->total = $total; $this->discount = $discount; } public function finalAmount(): float { return $this->discount->apply($this->total); } } // اضافه کردن نوع تخفیف جدید بدون تغییر کلاس Order class FixedDiscount implements Discount { private float $deduct; public function __construct(float $deduct) { $this->deduct = $deduct; } public function apply(float $amount): float { return max(0, $amount - $this->deduct); } } |
پاسخ سوال ۹
Dependency Injection به معنی جدا کردن ساخت و فراهمسازی وابستگیهای یک کلاس از خود کلاس است. این الگو باعث میشود کلاسها مستقل، تستپذیر و قابل نگهداری شوند.
روشهای پیادهسازی DI:
- Constructor Injection
- وابستگیها از طریق سازنده (constructor) به کلاس تزریق میشوند.
- مزایا: وابستگیها هنگام ایجاد شیء وجود دارند و کلاس هرگز بدون آنها ساخته نمیشود.
- Setter Injection
- وابستگیها از طریق متدهای setter جداگانه پس از ایجاد شیء تنظیم میشوند.
- مزایا: انعطاف بیشتر در ساخت و امکان تغییر وابستگی در طول عمر شیء.
- معایب: ممکن است فراموش شود که setter فراخوانی شود.
- Interface Injection
- وابستگیها از طریق یک اینترفیس تامین میشوند؛ کلاس باید متدی تعریفشده توسط اینترفیس را پیاده کند تا DI انجام شود.
- کمتر رایج است اما صراحت قرارداد را بالا میبرد.
مثال Constructor Injection:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php interface MailerInterface { public function send(string $to, string $body): bool; } class SmtpMailer implements MailerInterface { public function send(string $to, string $body): bool { // منطق ارسال ایمیل با SMTP return true; } } class UserRegistration { private MailerInterface $mailer; public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } public function register(array $userData): void { // منطق ثبتنام $this->mailer->send($userData['email'], "Welcome!"); } } // تزریق وابستگی هنگام ساخت شیء $mailer = new SmtpMailer(); $registration = new UserRegistration($mailer); $registration->register(['email' => 'user@example.com']); |
پاسخ سوال ۱۰
Just-in-Time (JIT) compilation در PHP 8 مکانیزمی است که بخشی از فرآیند اجرای کد را به کد ماشین تبدیل میکند تا در زمان اجرا (at runtime) عملیات پردازشی با سرعت بالاتری انجام شود. PHP 8 JIT را روی بستر OPcache پیادهسازی کرده است:
- نحوه کار
- در حالت عادی، PHP اسکریپتها را به بایتکد کامپایل و در OPcache ذخیره میکند.
- با JIT، «گرمترین» بخشهای بایتکد (hot paths) شناسایی و در زمان اجرا به ماشینکد تبدیل میشوند.
- این تبدیل باعث میشود تکرار فراخوانیهای مکرر تابع یا حلقههای سنگین با سرعت بیشتری انجام شوند.
- مزایا
- بهبود کارایی برای کارهای محاسباتی هزاران بار تکرارشونده (CPU-bound).
- کاهش سربار مفسر در هر بار اجرای فانکشنها یا عملیات ریاضی پیچیده.
- امکان استفاده از PHP در موارد علمی و عددی (مانند شبیهسازیها) که قبلاً برای آن مناسب نبود.
- محدودیتها
- برای اپلیکیشنهای وب معمول که بیشتر I/O-bound هستند (دیتابیس، شبکه)، تفاوت چشمگیر نیست.
- مصرف حافظه و حجم OPcache افزایش مییابد.
- رفع اشکال (debug) در مسیرهای JIT شده پیچیدهتر میشود.
- نیاز به تنظیم دقیق (tuning) در
php.ini
با گزینههایی مانند:
1 2 3 |
opcache.enable=1 opcache.jit_buffer_size=100M opcache.jit=1255 |
سخن آخر
سوالاتی که در بالا مطرح شد جزء سوالات پیشرفته در زبان برنامه نویسی php هستند ، تسلط بر این ده سوال گرچه نقطهی شروع راه شما در مسیر ارشد شدن در دنیای PHP است، اما موفقیت واقعی زمانی رقم میخورد که با تمرین مداوم، مطالعهی مستندات رسمی و مشارکت در پروژههای عملی، این مفاهیم را در لایههای عمیقتری از کدنویسی و معماری نرمافزار بهکار بگیرید. اجازه دهید هر سؤال، پلی باشد برای کشف روشهای نوآورانه و بهبود کیفیت کدهای شما. با پیشروی در این مسیر، نه تنها مهارت فنی شما ارتقا مییابد، بلکه درک عمیقتری از اصول طراحی، امنیت و عملکرد بهدست خواهید آورد ، اگر نیاز به سفارش برنامه نویسی php دارید می توانید روی ایده پردازش حساب کنید .
همواره از به اشتراک گذاشتن تجربیاتتان در جامعه PHP دریغ نکنید و یاد بگیرید تا بیاموزید—این چرخهی سازنده کلید رشد حرفهای شماست.