0Pricing
PHP Academy · درس

شرح المنافذ والمحوّلات

اعزل النواة باستخدام منافذ ومحوّلات قابلة للتبديل.

شرح المنافذ والمحوّلات درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.

فكرة الشكل السداسي

ترسم المنافذ والمحوّلات — أي البنية السداسية (Hexagonal Architecture) التي صممها Alistair Cockburn — تطبيقكم على شكل سداسي. يوجد منطق الأعمال الخالص في الداخل. وكل تفاعل مع العالم الخارجي (HTTP وقاعدة البيانات والطابور والساعة والبريد الإلكتروني) يعبر منفذًا، ويُلبّى كل منفذ عبر محوّل واحد أو أكثر. ولا يملك الشكل جانبًا علويًا أو سفليًا مميزًا: فواجهة المستخدم وقاعدة البيانات متماثلتان، وكلتاهما مجرد محوّلين.

المنافذ هي الواجهات

المنفذ واجهة تملكها نواة التطبيق، وتعبّر عن حاجة أو قدرة بمصطلحات المجال. ويجب ألا تسرّب مفردات البنية التحتية — فلا PDOStatement ولا GuzzleResponse ولا Eloquent.

<?php
// Driven (outbound) port: the core needs to persist users
interface UserRepository
{
    public function byId(UserId $id): ?User;
    public function save(User $user): void;
}

المنافذ القائدة والمقودة

هناك نوعان:

  • المنافذ القائدة (الأولية، الواردة) — وهي API التي يستدعيها العالم الخارجي لقيادة التطبيق. وعادةً ما تكون واجهات حالات الاستخدام.
  • المنافذ المقودة (الثانوية، الصادرة) — وهي الواجهات التي يستدعيها التطبيق للوصول إلى العالم الخارجي: المستودعات ومرسلات البريد والساعات.

تستدعي المحوّلات القائدة النواة؛ بينما تستدعي النواة العالم الخارجي عبر المحوّلات المقودة.

<?php
// Driving (inbound) port — the public capability of the core
interface RegisterUser
{
    public function handle(string $email, string $plainPassword): UserId;
}

تنفّذ النواة المنافذ القائدة

تنفّذ حالة الاستخدام منفذًا قائدًا وتعتمد على منافذ مقودة. لاحظوا أنها تقبل PasswordHasher وClock بوصفهما منفذين محقونين — فحتى الوقت والتجزئة مجردان، لتبقى النواة حتمية وقابلة للاختبار.

<?php
final class RegisterUserService implements RegisterUser
{
    public function __construct(
        private UserRepository $users,
        private PasswordHasher $hasher,
        private Clock $clock,
    ) {}

    public function handle(string $email, string $plain): UserId {
        if ($this->users->byEmail($email)) {
            throw new EmailAlreadyTaken($email);
        }
        $user = User::register(
            $email,
            $this->hasher->hash($plain),
            $this->clock->now()
        );
        $this->users->save($user);
        return $user->id();
    }
}

محوّل مقود

يطبّق المحوّل المقود منفذًا مقودًا باستخدام تقنية فعلية. وهنا يلبي محوّل PDO متطلبات UserRepository. ويمكن استبداله بـ Doctrine أو Redis أو عميل API عبر HTTP دون المساس بالنواة.

<?php
final class PdoUserRepository implements UserRepository
{
    public function __construct(private PDO $pdo) {}

    public function byId(UserId $id): ?User {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([(string) $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ? User::fromRow($row) : null;
    }
    public function save(User $user): void {
        // INSERT ... ON CONFLICT UPDATE
    }
}

محوّل قائد

يترجم المحوّل القائد محفزًا خارجيًا إلى استدعاء لمنفذ قائد. فمتحكم HTTP وأمر CLI ومستهلك الرسائل — كلها محوّلات قائدة قابلة للتبديل لحالة الاستخدام نفسها.

<?php
// CLI driving adapter
final class RegisterUserCommand
{
    public function __construct(private RegisterUser $register) {}

    public function run(array $argv): int {
        [$email, $password] = array_slice($argv, 1);
        $id = $this->register->handle($email, $password);
        fwrite(STDOUT, "Created user $id\n");
        return 0;
    }
}

محوّلات داخل الذاكرة للاختبارات

أكبر فائدة هي أن لكل منفذ مقود بديلًا وهميًا سريعًا. تختبر الاختبارات حالة الاستخدام الحقيقية باستخدام محوّلات داخل الذاكرة، وساعات حتمية، ومجزّئ لا ينفّذ أي عملية.

<?php
final class FixedClock implements Clock {
    public function __construct(private DateTimeImmutable $t) {}
    public function now(): DateTimeImmutable { return $this->t; }
}
final class PlainHasher implements PasswordHasher {
    public function hash(string $p): string { return 'h:' . $p; }
}

$service = new RegisterUserService(
    new InMemoryUsers(),
    new PlainHasher(),
    new FixedClock(new DateTimeImmutable('2026-01-01'))
);
echo 'wired OK', PHP_EOL;

المحوّلات تترجم ولا تقرّر

من الأخطاء الشائعة السماح بتسرّب قواعد الأعمال إلى المحوّلات. والقاعدة العامة هي أن المحوّل يقتصر على ترجمة تنسيقات البيانات والبروتوكولات. إذا وجدتم عبارة if تتعلق بالتسعير أو الأهلية أو الحالة داخل متحكّم أو مستودع، فهي تنتمي إلى النواة.

  • الربط بين JSON وDTO: محوّل
  • ترطيب الكيان من SQL: محوّل
  • "يحصل العميل VIP على خصم 10%": النواة

منفذ واحد، ومحوّلات متعددة

تتيح المنافذ الاستبدال، بل وتتيح أيضاً استخدام محوّلات متوازية. يمكن أن يضم NotificationPort محوّلات للبريد الإلكتروني والرسائل النصية وSlack تعمل معاً. تستدعي النواة طريقة واحدة، بينما يحدّد الربط عدد القنوات التي تستجيب.

<?php
interface Notifier { public function send(string $to, string $msg): void; }

final class CompositeNotifier implements Notifier {
    /** @param Notifier[] $channels */
    public function __construct(private array $channels) {}
    public function send(string $to, string $msg): void {
        foreach ($this->channels as $c) $c->send($to, $msg);
    }
}

$notifier = new CompositeNotifier([new EmailNotifier(), new SmsNotifier()]);
echo 'composed', PHP_EOL;

كيف يُترجم الشكل السداسي إلى مجلدات

تنظيم عملي بلغة PHP لسياق محدود:

  • Domain/ — الكيانات، وكائنات القيم، وخدمات المجال
  • Application/Port/In/ — واجهات منافذ القيادة (حالات الاستخدام)
  • Application/Port/Out/ — واجهات المنافذ المُقادَة (المستودعات، والساعة)
  • Application/ — تطبيقات حالات الاستخدام
  • Infrastructure/Adapter/In/ — المتحكّمات، وواجهات CLI، والمستهلكون
  • Infrastructure/Adapter/Out/ — محوّلات PDO/Doctrine/HTTP

يربط جذر التركيب (إعداد حاوية DI) المحوّلات In وOut بالمنافذ.

اختبار الشكل السداسي بالكامل

إلى جانب اختبارات الوحدة، تتيح المنافذ إجراء اختبارات قبول سريعة تقود التطبيق عبر منفذه الأساسي، وتتحقق من النتائج عبر محوّلات ثانوية داخل الذاكرة، فتغطي حالة استخدام كاملة من دون HTTP أو قاعدة بيانات. ويمكن لاحقاً تشغيل مجموعة الاختبارات نفسها باستخدام محوّلات حقيقية كاختبارات تكامل، مما يمنحكم استراتيجية اختبار متعددة الطبقات من دون إعادة كتابة.

<?php
// Acceptance test: real use case, fake driven adapters, no I/O
$users = new InMemoryUsers();
$service = new RegisterUserService($users, new PlainHasher(),
    new FixedClock(new DateTimeImmutable('2026-01-01')));

$id = $service->handle('dev@coddykit.com', 'pw');

assert($users->byId($id) !== null);
echo 'acceptance: user persisted via in-memory adapter', PHP_EOL;

تحقّق سريع

أي العبارات التالية صحيحة بشأن المنافذ والمحوّلات؟

مراجعة

يعزل نمط المنافذ والمحوّلات النواة خلف واجهات:

  • المنافذ هي واجهات بلغة المجال، وتملكها النواة.
  • منافذ القيادة تستدعيها المحوّلات الواردة؛ أما المنافذ المُقادَة فتستدعيها النواة وتلبّيها المحوّلات الصادرة.
  • تقتصر المحوّلات على ترجمة البروتوكولات والتنسيقات، ولا تتخذ قرارات أعمال مطلقاً.
  • يدعم المنفذ نفسه محوّلات متعددة، مثل البدائل الوهمية للاختبارات والمحوّلات المركّبة لتوزيع الاستدعاءات.
  • يربط جذر التركيب كل شيء، بينما يظل الشكل السداسي مستقلاً عن أطر العمل.

الأسئلة الشائعة

هل درس «شرح المنافذ والمحوّلات» مجاني؟

نعم — نص درس «شرح المنافذ والمحوّلات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.

ماذا ستتعلم في «شرح المنافذ والمحوّلات»؟

اعزل النواة باستخدام منافذ ومحوّلات قابلة للتبديل. تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟

لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «شرح المنافذ والمحوّلات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟

نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. من البنية متعددة الطبقات إلى البنية النظيفة
  2. شرح المنافذ والمحوّلات
  3. حالات الاستخدام وخدمات التطبيق
  4. عكس الاعتماديات في التطبيق
← العودة إلى PHP Academy