مدونة عمار الخوالده

شرح برمجة Package لـ PHP ورفعها على Packagist


شرح برمجة Package لـ PHP ورفعها على Packagist

مقدمة

يستخدم المبرمج أثناء العمل كثيرا من الـ Packages المختلفة، الـ Package هي حزمة أو مجموعة من الكلاسات Classes والدوال Functions القابلة لإعادة الاستخدام دون الحاجة لإعادة برمجتها مرة بعد أخرى.

شرحت في مقال سابق مدير الاعتماديات Composer وكيف يستخدم لإدارة الـ Packages التي تستخدمها في مشروعك، لكن هل فكرت من قبل بإمكانية برمجة Package خاصة بك بحيث تستخدمها في مشاريعك أو تشاركها مع غيرك ليستخدموها؟

لنفترض أن لديك هذا الكلاس الذي يرسل Http Requests:

 1<?php
 2
 3class JsonHttpClient
 4{
 5    public static function get(string $url): array
 6    {
 7        $curlHandle = curl_init();
 8
 9        curl_setopt_array($curlHandle, [
10            CURLOPT_URL => $url,
11            CURLOPT_RETURNTRANSFER => true,
12        ]);
13
14        $response = curl_exec($curlHandle);
15
16        curl_close($curlHandle);
17
18        return json_decode($response, true);
19    }
20}

الكلاس جاهز للاستخدام، لكن بدلا من نسخه في كل مشروع تحتاجه فيه، تريد أن تحوله إلى Package، وهذا سيساعدك أيضا على تحديث الـ Class، فلو أضفت له Method جديدة، أو حتى أضفت كلاس آخر، يمكنك بسهولة تحديث الـ Package في جميع المشاريع التي تستخدمها.

إنشاء الـ Package

قم بإنشاء Directory لمشروعك، ثم نفذ composer init، سيقوم الأمر بطلب بعض المعلومات منك:

صورة نتيجة تنفيذ الأمر composer init

بعض هذه المعلومات التي يطلبها لم يتم إدخالها لأنها غير مهمة أو سنقوم بشرحها لاحقا خلال المقال، كما يطلب منك تحديد الـ Dependencies أو الـ Packages الأخرى التي يعتمد عليها مشروعك، لا أفضل استخدام أداة البحث عن الـ Dependencies بل أفضل تثبيت ما احتاجه لاحقا خلال التطوير.

ستجد الآن ملف composer.json جديد:

 1{
 2    "name": "ammardev/json-http-client",
 3    "description": "A package for sending HTTP requests",
 4    "authors": [
 5        {
 6            "name": "Ammar Al-Khawaldeh",
 7            "email": "me@ammar.dev"
 8        }
 9    ],
10    "require": {}
11}

لاحظ أن اسم الـ Package يتكون من جزئين، الجزء الأول هو الـ Vendor Name، قد يكون اسمك، أو اسم الشركة التي تعمل على الـ Package.

يُنصح باختيار اسم المستخدم لحسابك على Github (او اسم الـ Organization) كإسم الـ Vendor

كتابة الكود الخاص بالـ Package

يمكنك كتابة الـ Code الخاص بك بأي ترتيب للمجلدات، لكن في العادة يكتب الكود الخاص بالـ Package في مجلد src، اسم المجلد لا يمثل أي شيء مميز متعلق بـ Composer، لكنه الاسم المتعارف عليه في الغالب.

بعد إضافة الكلاس الخاص بنا، تصبح ملفات المشروع كالآتي:

.
├── src
│   └── JsonHttpClient.php
└── composer.json

استخدام الـ Namespace

تستخدم الـ Namespace لتنظيم المشروع، إضافة إلى التفريق بين الكلاسات المختلفة اذا تشاركت بنفس الاسم، في حالة تطوير الـ Packages، لا بد من استخدام الـ Namespace منعا لأي مشاكل قد تنتج في حال استخدام Packages اخرى تحتوي على كلاسات بنفس الاسم.

طبقا لـ PSR-4 فإن الـ Autoloader (الذي سبق وتكلمنا عنه في المقال السابق) يقوم تلقائيا بمعرفة مسار الملف (File Path) اعتمادا على الـ Namespace.

لكننا لا نريد لاسم مجلد src أن يظهر في الـ Namespace، فالهدف منه تنظيمي فقط كمأ اننا قد نرغب باضافة اسم الشركة أو المطور الذي يعمل على الميزة (Vendor Name) لتمييز الـ Namespace بشكل أسرع.

يمكن تعديل اعدادات الـ Autoloader بحيث نلغي اسم المجلد src ونستخدم بدلا منه اسما آخر، قمت بإضافة الخيار autoload إلى composer.json:

1    "autoload": {
2        "psr-4": {"AmmarDev\\HttpClient\\": "src/"}
3    }

ثم سأضيف للكلاس السابق اسم الـ Namespace:

1namespace AmmarDev\HttpClient;

بهذه الطريقة يتعرف الـ Autoloader على أن أي NameSpace تبدأ بـ AmmarDev\HttpClient تمثل مجلد src وليس مجلدا بنفس اسم الـ Namespace.

مستودعات الحزم

يتم رفع الـ Packages على مستودعات، المستودعات قد تكون على سيرفرات خاصة لا يمكن الوصول إليها الا بـ Token محددة، او قد تكون على سيرفرات عامة. أغلب الـ Packages مفتوحة المصدر تُرفع على الـ Repository الافتراضي لكومبوزر وهو Packagist.

على موقع Packagist يمكنك البحث عن الـ Packages ومعرفة وصفها والمشاركين في تطويرها (وهي معلومات يتم ادخالها في composer.json.

افتراضيا، عند وجود أي Package في قائمة require او require-dev يتم تثبيتها من Packagist، ما لم تقم بإضافة Repositories إضافية.

توجد عدة أنواع من الـ Repositories، في هذا المقال سنشرح نوعا إضافيا واحدا فقط وهو path، هذا النوع يقوم بتثبيت الـ Packages من مسار معين على جهازك بدل تثبيته من سيرفر على الويب.

يمكنك التعرف على تفاصيل أكثر عن الـ Repositories وأنواعها من توثيق Composer

هذا النوع مفيد أثناء تطوير الـ Package على جهازك حيث لا تضطر لرفع الـ Package عند كل تعديل.

استخدام الـ Package عن طريق الـ Path Repository

سأقوم الآن بانشاء مشروع جديد يتكون من ملف index.php وملف composer.json، يمكنك انشاء ملف composer.json يدويا أو باستخدام الأمر composer init كما وضحنا سابقا.

ستتم اضافة Array باسم repositories إلى ملف composer.json فيه Object واحد لمسار الـ Package التي قمنا بانشائها:

1  "repositories": [
2    {
3      "type": "path",
4      "url": "../php-packages/json-http-client"
5    }
6  ]

أصبح الآن بإمكاننا بعد إضافة الـ Repository إلى composer.json أن نثبت الـ Package:

1    "require": {
2        "ammardev/json-http-client": "dev-main"
3    }

يختلف هذا عن التثبيت الاعتيادي للحزم المستقرة المخصصة للعمل على الـ Production بعدم وجود نسخة، وبدلا من النسخة يتم كتابة dev-main التي سيتم شرحها لاحقا في هذا المقال، قم الآن بتنفيذ composer install لتثبيت الحزمة.

يمكنك الآن استخدام الـ Package بشكل طبيعي، هكذا يبدو index.php بعد استخدام الـ Package:

1<?php
2
3require './vendor/autoload.php';
4
5use AmmarDev\HttpClient\JsonHttpClient;
6
7var_dump(JsonHttpClient::get('https://dummyjson.com/todos/1'));

هل تحتاج لتحديث الـ Package كلما قمت بتعديلها؟

تعمل الـ Path Repository عن طريق إنشاء Symbolic Link بين مجلد الـ Package وبين مجلد الـ vendor داخل مشروعك، مما يعني أن أي تعديل على الـ Package يتم تطبيقه مباشرة دون الحاجة لتنفيذ composer update.

على الرغم من عدم الحاجة لتحديث الحزم عند التعديل، إلا أن بعض خيارات composer.json مثل خيار الـ autoload لن تعمل بشكل صحيح اذا تم تغييرها لأن الخيار لم يكن موجودا عند تثبيت الـ Package، يمكن حل هذا النوع من المشاكل بتنفيذ composer dumpautoload الذي يقوم باعادة توليد ملف الـ autoload.

كيف تعمل نسخ Composer؟

يعتمد Composer في تحديد رقم النسخة على الـ Git Tags، الـ Tag قد لا يحدد النسخة فقط، بل قد يحدد أيضا الـ Stability Level (ترجمة حرفية: مستوى الاستقرار)، فالـ Tag الآتي: v2.0-beta يحدد النسخة، ويحدد أن هذه النسخة ليست مستقرة بعد، بل هي نسخة تجريبية beta.

افتراضيا، يقوم composer بتثبيت النسخ المستقرة “Stable”.

ماذا عن نسخة dev-main التي قمنا باستخدامها في المثال السابق؟

dev- التي في البداية تعني اننا سنسمح لـ composer باستخدام مستوى dev، يسمح لنا Composer في هذا الـ Stability level أن نستخدم اسم الـ Git Branch بدلا من الـ Tag، في هذه الحالة main (أو master) هو اسم الـ Branch الرئيسي للـ Package، فنحن هنا نحدد الـ Branch بدلا من تحديد النسخة.

في حالة اختيار مستودع من نوع VCS (مثلا git)، سيتم تحميل النسخة المطلوبة بحسب الـ Branch، أما في حالة استخدام النوع Path، سيتم اخذ جميع التعديلات حتى لو لم يتم عمل commit بعد (أو حتى لو لم تتم تهيئة git بعد)

انشاء نسخة جديدة من الـ Package ونشرها على Packagist

قم بالتسجيل في Packagist وربطه بحسابك على Github أولا، ثم اضغط على Submit Package:

صورة للـ Package Submit في Packagist

بعد الضغط على Submit، يتم انشاء صفحة للـ Package على Packagist

صورة صفحة الـ Package على Packagist

توجد ازرار للتحكم بالـ Package على Packagist، تلاحظ أيضا وجود تحذير متعلق بالـ Auto Update، عند كل تحديث او تعديل على الـ Package عليك الضغط على زر Update، أو انشاء Github Webhook لتشغيل خاصية الـ Auto Update. لاحظ أيضا على اليمين وجود نسخة واحدة وهي dev-main.

لإنشاء نسخة جديدة، قم بانشاء Tag جديد، تأكد من اتباع طريقة التسمية الخاصة بـ Semantic Versioning، سأقوم الآن بانشاء نسخة جديدة للـ Package التي قمت برفعها:

1git tag v1.0.0

ثم نرفع الـ tags إلى الـ Repository:

1git push --tags

لاحظ التغير الآن على الموقع (قم بالضغط على Update أولا إن لم تقم بتفعيل الـ Auto Update):

نسخة الـ Package

النسخة الملونة هي النسخة الافتراضية التي سيختارها Composer (اذا وجدت اكثر من نسخة تنطبق عليها الـ Constrains).

هذا رابط الـ Package التجريبية المستخدم في هذا المقال: ammardev/json-http-client - Packagist

اقرأ أيضا