اذهب إلى المحتوى

عبد اللطيف ايمش

الأعضاء
  • المساهمات

    1406
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    63

كل منشورات العضو عبد اللطيف ايمش

  1. التجريد – Abstraction نعلم أنَّ الصنف المُشتَق يأخذ خاصياته من الصنف الأب لكن الصنف المُشتَق مستقل تمامًا عن الصنف الأب؛ وقد يكون في بعض الأحيان من الجيد أن نرسم خطوطًا عريضة لآلية سلوك الصنف الابن، وهذه هي مهمة الأصناف والدوال المجردة. إذ أنَّ الصنف المجرد يحتوي على دوال غير مكتملة (أي مجردة) التي يجب أن يملأها الابن لكي يكون صنفًا وعدا ذلك سيكون صنفًا مجردًا أيضًا. بكلامٍ آخر، الصنف المُجرَّد (Abstract Class) هو صنف يحتوي على أسماء دوال دون كتابة الشيفرات المسؤولة عن عملها وتسمى تلك الدوال بالدوال المجردة، وقد يحتوي أيضًا على دوال كاملة اعتيادية تؤدي وظيفتها تمامًا. انظر إلى المثال الآتي لمزيدٍ من الإيضاح: <?php // تعريف صنف مجرد abstract class AbsClass { function __construct() { echo "this is an abstract class <br>"; } // دالة مجردة abstract public function abs_function(); function full_function() { echo "this is not an abstract function <br>"; } } class SubClass extends AbsClass { function __construct() { echo "this is child class <br>"; parent::full_function(); } // تعريف الدالة المجردة public function abs_function() { echo "this is completed abstract function <br>"; } } $obj = new SubClass(); $obj->abs_function(); ?> نستعمل الأصناف المجردة عندما يلزمنا إنشاء طريقة محددة للتعامل مع عدِّة أصناف مُشتقَّة، التي قد تتشارك ببعض الوظائف. ملاحظة: لا يمكن إنشاء كائن من صنف مجرد، حيث لا يمكن إلا اشتقاق تلك الأصناف. يستعمل الصنف المجرد لتقييد عمل الصنف الابن. الواجهات interfaces من بين الحالات التي نستعمل فيها الواجهات (interfaces) هي عندما نريد تطبيق التعددية الشكلية أي أن تكون طريقة تعاملنا متشابهة مع عدِّة أصناف. الواجهة هي مجموعة من الدوال المجردة أي أنك تعرف اسم الدالة مع المعاملات التي تقبلها لكن دون تحديد طريقة عمل الدالة، ويمكن للصنف أن يستعمل أكثر من واجهة، لكن يجب أن يعيد تعريف كل الدوال الموجودة في تلك الواجهة، انظر إلى المثال الآتي لأخذ فكرة عن الواجهات: <?php // تعريف واجهة interface MyInterface { // abstract functions // all must be public public function display(); public function another($argument); } // واجهة أخرى interface AnotherInterface { public function complete_it(); public function one_more(); } class Parent { function parent_fun() { echo "parent function"; } } // صنف يشتق صنفًا آخر ويستعمل واجهة class Demo extends Parent implements MyInterface, AnotherInterface { public function display() { echo "display complete"; } public function another($argument) { #code } public function complete_it() { #code } public function one_more() { #code } } ?> نستعمل الواجهات عندما نريد إنشاء طريقة موحدة للتعامل مع عدِّة أصناف، فمثلًا، نُنشِئ واجهة اسمها Database فيها دوال مجردة مثل select و insert وغيرها، ثم نستعمل تلك الواجهة في صنف MySQL وفي صنف SQLite، ونعيد تعريف الدوال الموجودة في الواجهة بما يلائم طريقة عمل كل نوع من أنواع قواعد البيانات. وبهذه الطريقة نستطيع أن نستعمل قواعد بيانات MySQL أو SQLite بنفس الآلية تمامًا. ملاحظة: يجب أن تكون جميع الدوال داخل الواجهة عامةً، يمكن أن يرث صنفٌ ما صنفًا آخر ويستعمل واجهة بنفس الوقت، لكن يجب أن يكون تعريف الاشتقاق قبل الواجهات. السمات Traits قدم الإصدار 5.4.0 من PHP ميزة السّمات Traits، وهي طريقة تسمح بإعادة استعمال الشيفرات في اللغات التي لا تسمح بالوراثة المتعددة، وهي تقلل من المحدوديات الناتجة عن عدم السماح بالوراثة المتعددة عن طريق إتاحة استعمال مجموعة من الدوال في عدة أصناف. أي لو كانت عندك مجموعة من الدوال العامة، وترغب في مشاركتها بين أكثر من صنف، فضعها في Trait ثم استعملها (use) في تلك الأصناف. يُعرف Trait عبر ذكر الكلمة المحجوزة trait متبوعةً باسمه، ثم تُعرَّف الدوال داخله. وتُستعمل الكلمة use عند تعريف صنف يستعمل Trait معين كما في المثال الآتي: <?php trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class World { use HelloWorld; } $obj = new World(); $obj->sayHello(); // ستُطبع عبارة Hello World! ?> يمكن إعادة تعريف الدوال داخل الأصناف التي تستعمل Trait معيّن، كما في المثال الآتي: <?php trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class World { use HelloWorld; } class NewWorld { use HelloWorld; public function sayHello() { echo 'Hello Universe!'; } } $obj1 = new World(); $obj1->sayHello(); // ستُطبع عبارة Hello World! $obj2 = new NewWorld(); $obj2->sayHello(); // ستُطبع عبارة Hello Universe! ?> يمكن استعمال أكثر من Trait في نفس الصنف عبر ذكر أسمائهم في عبارة use مفصولًا بينهم بفاصلة، لاحظ أنه إذا عُرِّفَت دالتين بنفس الاسم في أكثر من Trait، ثم استعملناها في صنف، فسيحدث تضارب وتظهر رسالة خطأ fetal error، ويمكنك حل مشكلة التضارب في الأسماء عبر استعمال المعامل insteadof أو عبر as كما في المثال الآتي: <?php trait A { public function smallTalk() { echo 'a'; } public function bigTalk() { echo 'A'; } } trait B { public function smallTalk() { echo 'b'; } public function bigTalk() { echo 'B'; } } class Talker { // لدينا في A و B دالتين اسمهما bigTalk و smallTalk // ما يلي سيجعل الصنف يستعمل الدالة smallTalk من B عوضًا عن مثيلتها في A // و bigTalk من A عوضًا عن B use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; } } class Aliased_Talker { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; // لاحظ كيف استعملنا as لتغير اسم الدالة في الصنف B::bigTalk as talk; } } ?> مصادر مقالة Abstraction and Interface in PHP لصاحبها Harish Kumar فصل Objects في كتاب Practical PHP Programming فصل البرمجة غرضية التوجه في كتاب تعلم البرمجة بلغة PHP صفحات Object Interfaces و Traits في دليل PHP وغيرها.
  2. ستوفِّر قدرًا كبيرًا من الوقت عندما تطوِّر مواقع ووردبريس على خادوم محلي؛ فالتطوير على خادوم محلي له عدِّة مزايا أهمها أنه أكثر أمانًا وأسرع من لو أنَّك سترفع ملفاتك إلى الخادوم البعيد بشكل مستمر. المشكلة الوحيدة هي أنَّ نقل موقع ووردبريس إلى خادوم إنتاجي سيُسبِّب لك الصداع، فلا يحب أحدٌ أن يعبث مع جداول قاعدة البيانات. لحسن الحظ، عملية النقل أسهل مما تتصور ولن تأخذ من وقتك إلا القليل إن اتبعت الخطوات التي سنووردها في هذا الدرس. سأريك في هذا الدرس كيف تستعمل إضافة "Duplicator" لنسخ موقع ووردبريس يعمل على خادوم محلي إلى استضافة خارجية بسرعة وسهولة. هنالك طرقٌ مختلفة لنقل مواقع ووردبريس، لكن أسهلها هو استخدام إضافة Duplicator؛ وهذه الإضافة مشهورة جدًا في مستودع إضافات ووردبريس، بتقييم 4.9 من 5 وبأكثر من 700‎ 000 تنزيل. سأشرح -وسأريك- في الخطوات الآتية كيفية نقل ووردبريس من الخادوم المحلي عبر Duplicator، لكن قد تختلف بعض الخطوات اعتمادًا على الخادوم الإنتاجي الذي ستنقل إليه الموقع. ستحتاج إلى عميل FTP مع بيانات الدخول لخادومك الإنتاجي لكي تتبع الخطوات التي سنشرحها في هذا الدرس. من الجدير بالذكر أنَّك لا تحتاج إلى تثبيت ووردبريس على الخادوم الإنتاجي قبل اتباع هذه الخطوات، إذ سينسخ Duplicator كل ملفات ووردبريس اللازمة تلقائيًا. الخطوة الأولى: تحزيم موقعك باستخدام Duplicator نزِّل وثبِّت Duplicator على موقعك المحلي. هذه الإضافة مجانية ومتاحة في مستودع إضافات ووردبريس، لذا تستطيع أن تبحث عنها في صفحة الإضافات. بعد تثبيت وتفعيل الإضافة، اضغط على "Duplicator" في شريط أدوات لوحة التحكم، وستظهر شاشة "Duplicator Packages". ولأننا ثبتنا Duplicator منذ قليل فلن تكون هنالك أيّة حزم. حسنًا، ما هي الحزمة؟ تحتوي الحزمة على أرشيف لموقعك (الصيغة الافتراضية هي ZIP) وملف تثبيت مهمته أتمتة عملية ضبط الموقع المؤرشف على الخادوم الجديد. اضغط على Create New لإنشاء حزمة. ستسألك الصفحة التالية عن اسم الحزمة وتسمح لك بإضافة ملاحظات. الاسم الذي تمنحه لموقعك ليس ذا أهمية كبيرة، لكن عليك أن تعطيه اسمًا قابلًا للتذكر إن كنت تنوي أن تُنشِئ عدِّة حزم. أضف ملاحظاتك إن شئت. هنالك إعدادات اختيارية متعلقة بالأرشيف والمُثبِّت، لكنك تستطيع تجاهلها الآن. تسمح لك إعدادات الأرشيف بترشيح (filter) قاعدة البيانات الخاصة بك، بينما تسمح لك خيارات المُثبِّت بملء خيارات التثبيت التي كانت ستظهر لك عند تثبيت الحزمة المؤرشفة، مما يوفر عليك بعض الوقت في المستقبل. اضغط على Next للمتابعة. سيتفحَّص Duplicator نظامك ليتأكد أنَّ عملية بناء الحزمة ستتم بسلاسة دون مشاكل. يمكن أن تُساعد هذه الخطوة في تحديد أيّة مشاكل محتملة الوقوع؛ واجتياز هذا الخطوة يعني أنَّه ليس عليك فعل أيّ شيء لبناء الحزم، بينما يعني ظهور "تحذيرات" (Warn) أنَّك ستواجه مشاكل أثناء عملية بناء وتثبيت الحزمة. لم يكشف الفحص الذي أجراه Duplicator على الموقع الذي نحاول نقله في مثالنا عن أيّة مشاكل، سوى أنَّ المساحة التخزينية لملفات الصور كبيرة، لكنني سأتجاهل ذلك لأنه من غير المحتمل أن تُسبِّب الصور أيّة مشاكل في الخطوات المقبلة. اضغط الآن على Build لإنشاء الحزمة، وعندها ستبدأ الإضافة بأخذ نسخة احتياطية من موقعك. وكما ذكرتُ سابقًا، ستُنتِج إضافة Duplicator ملفين: أرشيف لموقعك (كملف ZIP مضغوط) وملف تثبيت (بصيغة PHP). حمّل كلا الملفين إلى سطح المكتب عندك. الخطوة الثانية: نسخ المثبت والأرشيف إلى الخادوم الإنتاجي علينا الآن نسخ ملفَي الأرشيف والتثبيت اللذان أنشأتهما إضافة Duplicator إلى الخادوم الإنتاجي لكي نستطيع البدء بعملية التثبيت. سأستخدم هنا برنامج Filezilla لنقلهما عبر FTP. عليك تسجيل الدخول إلى خادومك عبر FTP، ثم التنقل إلى مجلد public_html (أو أيًّا كان اسم المجلد الجذر لموقعك) ونسخ ملفَي الأرشيف والمُثبِّت من سطح مكتبك إلى ذاك المجلد؛ ربما ستأخذ هذه العملية بعضًا من الوقت، خصوصًا إذا كان ملف الأرشيف كبيرًا جدًا. الخطوة الثالثة: تثبيت الموقع على خادومك الإنتاجي الخطوة التالية هي تثبيت الموقع على الخادوم الإنتاجي، وعليك الوصول إلى ملف التثبيت الذي نسخته إلى خادومك بإضافة ‎/installer.php إلى اسم النطاق الخاص بك. مثلًا http://example.com/installer.php. ستظهر صفحة المُثبِّت أمامك طالبةً منك إدخال تفاصيل الدخول لقاعدة بيانات MySQL. إذا كنتَ ستستبدل موقع ووردبريس موجود مسبقًا (أي أنَّك تريد تحديث نسختك الحالية من الموقع) فعليك إدخال تفاصيل قاعدة البيانات الموجودة مسبقًا. أما إذا كنتَ تُنشِئ موقعًا جديدًا، فاضغط على Create New وأدخِل تفاصيل قاعدة البيانات الفارغة. لاحظ أنَّ بعض شركات الاستضافة لا تسمح باستخدام خيار إنشاء قاعدة البيانات، وهذا يعني أنَّ عليك إنشاء قاعدة البيانات يدويًا. هذه هي خطوات إنشاء قاعدة بيانات في لوحة تحكم cPanel: افتح MySQL Database أنشِئ قاعدة بيانات بالاسم الذي يحلو لك أنشِئ مستخدمًا جديدًا أضف المستخدم الجديد إلى قاعدة البيانات التي أنشَأتها امنح مستخدمك جميع امتيازات الوصول إلى قاعدة البيانات ثم اضغط على Make Changes املأ تفاصيل الدخول إلى قاعدة البيانات في صفحة التثبيت (لإضافة Duplicator) ثم اضغط على زر Test Connection لتتأكد أنَّ المُثبِّت يملك وصولًا إلى قاعدة البيانات الخاصة بك تأكد أنَّك ستحصل على رسالة Success لاختبارَي Server Connected (أي تم العثور على الخادوم) و Database Found (أي تم العثور على قاعدة البيانات) قبل أن تنتقل إلى الخطوة الآتية. وبهذا ستضمن عدم مواجهة مشاكل أثناء عملية التثبيت. اضغط على Close، ثم ضع إشارة "صح" على بند I have read all warnings and notices في أسفل الصفحة ثم اضغط على Run Deployment. سيبدأ المُثبِّت الآن بتثبيت موقعك على الخادوم الإنتاجي، لكن قد تستغرق هذه الخطوة بعض الوقت إن كان موقعك كبيرًا. سيسألك Duplicator أثناء التثبيت أن تتأكد من تفاصيل موقعك القديمة… اضغط على Run Update بعد أن تتأكد منها. الخطوات الأخيرة سيطلب Duplicator منك إكمال أربع خطوات قصيرة لكنها مهمة: Install Report (تقرير التثبيت): هذا تقريرٌ عن تفاصيل الأخطاء التي قد واجهت عملية التثبيت (أرجو أن لا تكون هنالك أيّة أخطاء!) وعدد الجداول والسجلات التي أنُشِئت (أو حُدِّثَت) في قاعدة البيانات كي تتأكد أنَّ إضافة Duplicator قد نسخت كل معلومات قاعدة البيانات التي تريدها. Save Permalinks (حفظ إعدادات الروابط الدائمة): اضغط على Save Permalinks لكي تؤخذ إلى موقعك الجديد حيث تستطيع ضبط إعدادات الروابط الدائمة. Test Site (اختبار الموقع): اضغط على Test Site وستُظهر لك واجهة الموقع على الخادوم الإنتاجي لكي تتحقق من أنَّ كل شيءٍ يعمل على ما يرام. File Cleanup (حذف ملفات التثبيت): هذه الخطوة مهمة جدًا وغرضها هو حذف ملف التثبيت وأيّة ملفات أخرى مرتبطة فيه التي أنشِئت أثناء عملية التثبيت وذلك لضروراتٍ أمنية. اضغط على File Cleanup لحذف تلك الملفات تلقائيًا. اختبر موقعك الجديد على الخادوم الإنتاجي هذا كل ما في الأمر، يجب أن تحصل الآن على نسخة مماثلة لموقعك الذي كنت تطوره على الخادوم المحلي. هل استخدمتَ Duplicator لنقل المواقع من قبل؟ إن لم تكن تستخدمه، فما هي أفضل طريقة لنقل المواقع برأيك؟ شاركنا تجربتك في التعليقات. ترجمة -وبتصرّف- للمقال The Quick and Easy Guide to Migrating a Local WordPress Installation to a Live Site لصاحبته Raelene Morey.
  3. كان الغرض من البرمجة الكائنية (Object oriented programing اختصارًا OOP) هو السماح للمبرمجين بتسهيل تقسيم البرامج حسب وظيفتها؛ فالمبرمجون يُنشؤون "كائنات" ويضبطون بعض الخاصيات ثم يطلبون من تلك الكائنات أن تقوم بأشياءٍ معيّنة. مثلًا لدينا "الأكواب" عبارة عن كائنات، وتلك الأكواب لها خاصيات معيّنة مثل المادة المصنوعة منها (زجاج، أو بلاستيك، أو معدن) والسعة القصوى لها، كما يمكن إجراء عمليات عليها مثل ملء كوب. لكن عمومًا تنطوي كل تلك الأنواع تحت لواء "الأكواب" وإن اختلفت خاصياتها. أنُشِئت الأصناف (classes) في PHP لغرض التجريد (abstraction) والتغليف (encapsulation). والصنف هو مجموعة من المتغيرات والدوال التي تؤدي وظيفة مشابهة؛ وتساعد الأصناف في تجنب المشاكل الأمنية وذلك بفصل الواجهة (interface) عن طريقة التطبيق (implementation)، وتضيف الأصناف قيودًا إلى الوصول إلى المتغيرات والدوال. يُعرَّف الصنف بكتابة الكلمة المحجوزة class يليها اسم الصنف، ومن المستحسن اتباع طرق الكتابة التقليدية في أسماء الأصناف، حيث يبدأ اسم الصنف بحرفٍ كبير؛ هذا مثالٌ عن تعريف صنف بسيط: <?php class SimpleClass { // التعليمات البرمجية } ?> الشيفرة الموجودة ضمن الصنف لا تنفذ مباشرةً، إذ علينا أولًا أن قومة بإنشاء بإشاء كائن (object) من ذاك الصنف، الكائن هو نسخة من الصنف تكون جميع متغيرات ودوال ذاك الصنف جزءًا من خاصياتها (properties). يمكن إنشاء كائن من صنف كالآتي: object_name = new ClassName(); الخاصيات والدوال المتغيرات التي تكون عضوًا بالصنف تسمى خاصيات (properties)، وتُعرَّف عبر استخدام إحدى محددات الوصول public (عام) أو protected (محمي) أو private (خاص)، ويأتي بعدها التعريف الاعتيادي للمتغيرات، ويمكن أن تُسنَد القيم إليها مباشرةً، لكن يجب أن تكون تلك القيم ثابتة، أي لا تحتوي تعابير رياضية أو قيم معادة من دوال. أما الدوال الأعضاء في الأصناف، فتسمى توابع methods، وتُعرَّف كغيرها من الدوال، لكن مع الانتباه إلى ضرورة تحديد مجال الدالة (عامة أو محمية أو خاصة) كما في المثال الآتي: <?php class SimpleClass { // تعريف متغير أو خاصية public $var = 'a default value'; // تعريف دالة public function displayVar() { echo $this->var; } } ?> المتغير ‎$this متوفر داخل دوال الصنف تلقائيًا (أي ليس عليك إنشاؤه)، وهو يشير إلى الكائن الذي قام بإنشاء نسخة من الصنف، ويُستعمل للوصول إلى الخاصيات أو الدوال الموجودة في الصنف. لاحظ عدم وجود رمز الدولار ($) قبل اسم المتغير عند الوصول إليه عبر ‎$this. مثالٌ عن ما سبق: <?php class ClassName { // تعريف متغير public public $class_variable; // الدالة البانية function __construct() { $this->class_variable = 60 * 60; echo "this is the constructor <br>"; } // إعادة متغير في الصنف function get_global() { return $this->class_variable; } // تعديل متغير في الصنف function set_global($value) { $this->class_variable = $value; } // إظهار قيمة متغير في الصنف public function reset_display() { $this->private_function(); echo $this->get_global()." <br>"; } // دالة خاصة private function private_function() { $this->class_variable = 60 * 60; } } $object_name = new ClassName(); echo $object_name->get_global()."<br>"; $object_name->set_global(231); echo $object_name->get_global()."<br>"; $object_name->reset_display(); ?> لاحظ المفاهيم الآتية في المثال السابق التي شرحنا بعضها أعلاه: المتغير الذي يكون متاحًا للوصول في كل الصنف (‎$class_variable) يُعرَّف بالكلمة المحجوزة public؛ أما المتغيرات المحلية المُعرَّفة داخل دالة لا يمكن الوصول إليها إلا من تلك الدالة. يمكن الوصول إلى متغيرات أو دوال الصنف باستخدام ‎$this->variable_name;‎ و ‎$this->function_name();‎ على التوالي وبالترتيب. إذ أنَّ ‎$this يُشير إلى الصنف نفسه. الدالة البانية (constructor) هي دالة ذات معنى خاص في الأصناف؛ حيث تُشغَّل هذه الدالة عندما نُنشِئ كائنًا من ذاك الصنف، ويمكن أيضًا إرسال وسائط إلى الدالة البانية. سنشرح الدالة البانية والهادمة لاحقًا. ناتج السكربت السابق: it is constructor 3600 231 3600 ملاحظة: لا يُنصح بتعديل قيم خاصيات الفئات يدويًا، وإنما استعمل دوالًا خاصةً لهذا الغرض، فمثلًا لو كان عندك صنفٌ وظيفته حساب كمية الماء اللازمة لمدينة ما، ولديك خاصية اسمها population تُمثِّل عدد سكان المدينة، فلا تسمح بالوصول إليها مباشرةً، وإنما اكتب دالةً اسمها setPopulation مثلًا، واجعلها تُعدِّل قيمة عدد السكان وكل ما يتعلق بها من الحسابات تلقائيًا: $obj->setPopulation(200000); الوراثة نحاول دومًا عندما نبرمج ألّا نعيد كتابة الشيفرة مرارًا وتكرارًا، وأن نفصل بين تطبيق الشيفرة والواجهة (interface) لأسباب تتعلق بالحماية. الخيار الجديد المتاح أمامنا الآن هو استعمال الوراثة للقيام بالأمور السابقة بكفاءة. الوراثة في البرمجة كالوراثة في حياتنا، إذ يرث والدك بعض الخاصيات من جدك ويُعدِّلها أيضًا، وأنت أيضًا ترث بعض الخاصيات من والدك وتعدلها وتضيف غيرها. تسمى هذه العملية في البرمجة بالمصطلح inheritance. يمكن لأي صنف أن يوسِّع أو يشتق (extend) أصنافًا أخرى ويمكنه أن يصل إلى المتغيرات والدوال العامة والمحمية فيها فقط (أي لا يستطيع أن يصل إلى الدوال الخاصة private). <?php /** * الوراثة في PHP */ class Grandfather { // متغير عام public $global_variable; // الدالة البانية للصنف grandfather function __construct() { $this->global_variable = 56; echo "I am grandfather <br>"; } function default_function() { echo "this is default function in grandfather <br>"; } private function private_function() { echo "this is private function in grandfather <br>"; } protected function protected_function() { echo "this is protected function in grandfather <br>"; } public function public_function() { echo "this is public function in grandfather <br>"; } } /** * هذا صنف فرعي مشتق من الصنف Grandfather * وسيرث كل خاصياته عدا الخاصة (private) منها */ class Father extends Grandfather { // متغير عام public $father_var; function __construct() { // السطر الآتي مساوٌ للسطر => parent::__construct(); Grandfather::__construct(); $this->father_var = 256; echo "I am father <br>"; } public function display_all() { $this->default_function(); $this->protected_function(); $this->public_function(); echo "I am father's display_all <br>"; parent::public_function(); } } /** * هذا الصنف الابن يرث من الصنف الأب * ويرث أيضًا (بشكلٍ غير مباشر) من الصنف الجد */ class Child extends Father { // الدالة البانية في الصنف الابن function __construct() { Grandfather::__construct(); echo "I am child <br>"; } // يُعدِّل الابن في دالة موجودة في الأب // تسمى هذه العملية «إعادة تعريف الدوال» function display_all() { echo "function from father<br>"; // استدعاء دالة من الصنف الأب parent::display_all(); echo "new added in child<br>"; } } $obj = new Father(); $obj->display_all(); echo "<br><br><br>Child object call<br><br>"; $obj2 = new Child(); $obj2->display_all(); ?> الناتج: I am grandfather I am father this is default function in grandfather this is protected function in grandfather this is public function in grandfather I am father's display_all this is public function in grandfather Child object call I am grandfather I am child function from father this is default function in grandfather this is protected function in grandfather this is public function in grandfather I am father's display_all this is public function in grandfather new added in child يعطي المثال السابق صورةً كاملةً عن الوراثة، لنحاول فهمه: الصنف Child يرِث من الصنف Father، الذي بدوره يرث الصنف Grandfather؛ وهذا يُسمى الوراثة متعددة المستويات. الصنف الفرعي (subclass أي الصنف الذي يقوم بالوراثة) يمكنه الوصول إلى جميع الخاصيات غير الخاصة (private) للصنف الموروث (يسمى أيضًا superclass). يمكن للصنف الفرعي أن يستدعي دوال الصنف الموروث عبر استعمال الصيغة الآتية parent::function_name()‎ (يمكن استعمالها لمستوى وراثة وحيد فقط، أي يمكن للصنف Child أن يستعملها لاستدعاء دوال الصنف Father، ويمكن للصنف Father أن يستعملها للصنف Grandfather؛ لكن لا يمكن أن يستعملها الصنف Child لاستدعاء دوال الصنف Grandfather.) أو الصيغة الآتية SuperClass_name:function_name()‎ التي يمكن استعمالها لاستدعاء دوال الصنف Grandfather من الصنف Child. يمكن أن يُعدِّل الصنف الفرعي في دوال الصنف الأب، وذلك يُعرَف بإعادة التعريف (overriding). لا تدعم لغة PHP الوراثة المتعددة؛ أي لا يمكن للصنف أن يرث أكثر من صنف واحد. محددات الوصول لقد تطرقنا سابقًا إلى موضوع محددات الوصول بشكل مبسط، حان الوقت الآن لشرحها بالتفصيل. هنالك كلماتٌ محجوزةٌ تسمى محددات الوصول توضع قبل تعريف المتغيرات أو الدوال الأعضاء في الصنف، وتعيّن مَن الذي يستطيع الوصول إلى ذاك المتغير أو الدالة، وهي: public (عام): ذاك المتغير أو الدالة يمكن الوصول إليه من داخل الصنف أو من خارجه private (خاص): لا يمكن الوصول إلى المتغير أو الدالة إلا من داخل الصنف نفسه protected (محمي): يسمح بالوصول إلى المتغير أو الدالة من الصنف نفسه والصنف المُشتَق منه فقط final (نهائي): هذه الدالة لا يمكن إسناد قيمة أخرى إليه أو تعريفها في الأصناف المُشتقَة (لا يمكن استخدام final مع الخصائص/المُتغيّرات). نستعمل عادةً المُحدِّد public للخاصيات أو الدوال التي تريد الوصول إليها من خارج الصنف، أما private فتستعمل للأجزاء الداخلية التي لا يلزم الوصول إليها من خارج الصنف، أما protected فهي للخاصيات التي تستعمل في بنية الصنف (كما في private) لكن من المقبول توريثها إلى أصنافٍ أخرى كي يعدلوها بما يلائم. فمثلًا، لو كنا نكتب صنفًا للاتصال بقاعدة البيانات، فسيكون مقبض الاتصال (connection handle) لقاعدة البيانات مخزنًا في متغير خاص، لأنه يستعمل داخل الصنف فقط، ولا يجب أن يكون متوفرًا لمستخدم هذا الصنف؛ أما عنوان الشبكة لمضيف خادوم قواعد البيانات، فهو خاص بالصنف، ولا يجب على المستخدم تعديله، لكن من المقبول تعديله من صنفٍ يرث هذا الصنف. أما الطلبيات التي تُجرى على قاعدة البيانات، فيجب أن تكون عامة، كي يستطيع مستخدم الصنف الوصول إليها. الدالة البانية والدالة الهادمة ذكرنا الدالة البانية سابقًا ومررنا عليها سريعًا، وقلنا وقتها أنَّ الدالة البانية تُنفَّذ عند إنشاء كائن من الصنف، وهي مناسبة لعمليات تهيئة المتغيرات وإعطائها قيمةً ابتدائيةً. تحمل هذه الدالة اسم ‎__construct. يجدر بالذكر أنَّ الدالة البانية للصنف الأب لا تستدعى تلقائيًا عند إنشاء كائن للصنف الابن، ويجب استدعاؤها يدويًا عبر parent::__construct()‎ ضمن الدالة البانية للصنف الابن. لكن إن لم تُعرَّف دالة بانية للصنف الابن، فسيرث الدالة البانية للصنف الأب تلقائيًا. مثالٌ عليها سيوضح ما سبق: <?php class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } class OtherSubClass extends BaseClass { // سيرث هذا الصنف الدالة البانية للصنف الأب } // ستنفذ الدالة البانية للصنف BaseClass $obj = new BaseClass(); // ستنفذ الدالة البانية للصنف BaseClass // وستنفذ الدالة البانية للصنف SubClass $obj = new SubClass(); // ستنفذ الدالة البانية للصنف BaseClass $obj = new OtherSubClass(); ?> أما الدالة الهادمة، فهي الدالة التي تُنفَّذ عندما لا يعود الكائن موجودًا، وتُعرَّف -كما الدالة البانية- بذكر اسمها ‎__destruct، ولا تستدعى الدالة الهادمة للصنف الأب إن أُعيد تعريفها في الصنف الابن. <?php class MyDestructableClass { function __construct() { print "In constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "Destroying " . $this->name . "\n"; } } $obj = new MyDestructableClass(); // الناتج: In constructor echo "We will destroy \$obj \n"; unset($obj); // الناتج: Destroying MyDestructableClass echo '$obj Destroyed'; ?> معرفة نوع الكائنات لقد رأيت كيف أنَّ الوراثة هي أمرٌ مهمٌ في البرمجة الكائنية، ولكن قد تختلط عليك الكائنات، ولن تدري لأي صنفٍ تنتمي. يأتي الحل مع الكلمة المحجوزة instanceof التي يمكن استعمالها كأحد المعاملات، فستُعيد TRUE إن كان الكائن المذكور اسمه على يسارها تابعًا لصنفٍ ما أو لصنفٍ مشتقٍ من الصنف المذكور على يمينها. على سبيل المثال: $obj = new SubClass(); if ($obj instanceof SubClass) { } if ($obj instanceof BaseClass) { } ناتج العبارتان الشرطيتان السابقتان هو TRUE لأن ‎$obj هو كائنٌ ينتمي إلى الصنف SubClass (أول عبارة شرطية)، وينتمي إلى صنفٍ مشتقٍ من BaseClass (العبارة الشرطية الثانية). أما لو أردت أن تعلم إن كان الكائن ينتمي إلى صنفٍ مشتقٍ من الصنف المذكور، فاستعمل الدالة is_subclass_of()‎، الذي تقبل وسيطين، أولهما هو الكائن الذي سنختبره، والثاني هو اسم الصنف الأب. <?php class BaseClass {} class SubClass extends BaseClass {} $obj = new SubClass(); if ($obj instanceof SubClass) { echo 'instanceof is TRUE'; } if (is_subclass_of($obj, 'SubClass')) { echo 'is_subclass_of is TRUE'; } ?> ستجد أن instanceof في المثال السابق ستعطي TRUE، بينما ستعطي is_subclass_of()‎ القيمة FALSE، لأن ‎$obj ليس كائنًا لصنف مشتق من الصنف SubClass، وإنما هو من الصنف SubClass نفسه. تحديد الصنف في معاملات الدوال لنقل أنك تريد تمرير كائن من صنف معيّن إلى دالة، ولا تريد السماح بتمرير سلسلة نصية أو رقم لها بدلًا من الكائن ذي الصنف المحدد؛ تستطيع فعل ذلك بذكر اسم الصنف قبل المعامل أثناء تعريف الدالة، كما يلي: <?php class Numbers { public function print_random() { echo rand(, 10); } } class Chars {} function random (Numbers $num) { $num->print_random(); } $char = new Chars(); random($char); // fatal error: Argument 1 passed to random() must be an instance of Numbers ?> الدوال "السحرية" عندما تشاهد دالةً يبدأ اسمها بشرطتين سفليتين، فاعلم أنها دالة سحرية (Magic function)، وهي متوفرة من PHP، وتحجز PHP جميع أسماء الدوال التي تبدأ بشرطتين سفليتين على أنها دالة سحرية، لذا يُنصَح بتجنب استعمال هذا النمط من التسمية لتلافي حدوث تضارب في المستقبل مع الدوال السحرية التي قد تعرفها الإصدارات الحديثة من PHP. لقد رأينا سابقًا الدالتين البانية ‎__construct()‎ والهادمة ‎__destruct()‎، وسنتحدث هنا عن الدوال ‎__get()‎ و ‎__set()‎ و ‎__call()‎ و ‎__toString()‎. تُعرَّف الدالتان ‎__get()‎ و ‎__set()‎ داخل الأصناف، تستعمل الدالة ‎__get()‎ عندما تحاول قراءة متغير غير مُعرَّف من متغيرات الصنف، وتُستعمَل الدالة ‎__set()‎ عند محاولة إسناد قيمة لمتغير غير موجود، انظر إلى المثال الآتي: <?php class MagicClass { public $name = 'Magic'; private $age = 123; public function __get($var) { echo "getting: $var "; echo $this->$var; } public function __set($var, $value) { echo "setting: $var to $value"; $this->$var = $value; } } $obj = new MagicClass(); echo $obj->name; // الناتج: Magic $obj->age = 123; // أصبح بإمكاننا -باستعمال الدوال السحرية المُعرفة في الصنف- الوصول إلى متغير ذي وصولٍ خاص، لا يجدر بنا فعل ذلك عمومًا. echo $obj->age; // الناتج: getting: age 123 echo $obj->last_name; // ستحاول الدالة __get الحصول على قيمة الخاصية last_name، لكنها غير موجودة، وسيظهر خطأ من مرتبة Notice لإشارة إلى ذلك. ?> قد نستفيد من الدالتين ‎__get()‎ و ‎__set()‎ بإظهار رسالة خطأ عند محاولة الوصول إلى عناصر غير موجودة (أو لا يُمسَح لنا بالوصول إليها). أما دالة ‎__call()‎ فغرضها مشابه لغرض ‎__get()‎ إلا أنها تُستعمَل عند محاولة استدعاء دالة غير موجودة. ستستدعى الدالة السحرية ‎__toString()‎ في حال تمت محاولة طباعة الكائن كسلسلة نصية. هذه الدالة بسيطة جدًا وتعمل كما يلي: <?php class MagicClass { public function __toString() { return "I'm Magical! :-)"; } // ... } $obj = new MagicClass(); echo $obj; ?> مصادر مقالات Classes in PHP و Inheritance in PHP لصاحبها Harish Kumar.فص ل Objects في كتاب Practical PHP Programming. فصل البرمجة غرضية التوجه في كتاب تعلم البرمجة بلغة PHP. صفحات Introduction و The Basics و Constructors and Destructors و Visibility و Object Inheritance و Magic Methods و Type Hinting في دليل PHP وغيرها.
  4. الروابط الدائمة (permalinks) ذات المظهر الجميل هي الخيار الافتراضي لعناوين URL في مواقع ووردبريس، لكن ما الذي يجعل تلك الروابط "جميلةً"؟ وإذا كنتَ حديث العهد بووردبريس، فربما تتساءل: ما هي الروابط الدائمة؟ الروابط الدائمة، أو permalinks، هي عناوين URL للصفحات (pages) وللمنشورات (posts) وللتصنيفات (categories) وللأرشيفات (archives) في موقعك، وهي لا تتغير أبدًا، لذا تُستعمَل كرابط دائم للوصول إلى المحتوى الذي تُقدِّمه. الروابط الدائمة ذات البنية الجيدة مفيدة في جذب المستخدمين إلى موقعك بتسهيل تنقل الزوار ومحركات البحث في موقعك وإشارتهم إلى المحتوى الذي تقدمه. سنشرح في هذا الدرس كيف تعمل الروابط الدائمة في ووردبريس، وكيف تستطيع إدارة الإعدادات في لوحة التحكم لتحسين SEO، وبعض الإعدادات المتقدمة للتأكد أنَّ الروابط الدائمة ستعمل عملًا سليمًا على خادومك. الروابط الدائمة في ووردبريس هنالك ثلاثة أنواع رئيسية من الروابط الدائمة المتوفرة: الروابط الدائمة "القبيحة" وهذا هو الخيار الافتراضي في ووردبريس وتأخذ شكل عنوان URL للموقع ويليه عبارة تحتوي على مُعرِّف المنشور (post ID)، على سبيل المثال: http://www.example.com/?p=138 هذه الصيغة غير مقروءة للبشر (أي أنَّها تحوي أرقامًا بدلًا من كلماتٍ)، ولهذا سُمِّيت "القبيحة". الروابط الدائمة "نصف الجميلة" تُعرَف أيضًا باسم "PATHINFO permalinks"، وهي نسخةٌ مطوَّرةٌ من الروابط الدائمة القبيحة، حيث تتضمن السلسلة النصية index.php بعد اسم النطاق متبوعةً بُمعرِّف يُحدِّد المنشور الهدف؛ على سبيل المثال: http://www.example.com/index.php/yyyy/mm/dd/post-name/ ‎ الروابط الدائمة "الجميلة" هي تلك الروابط شائعة الاستعمال في المواقع العصرية، والتي تألف رؤيتها في ووردبريس وغيرها؛ وفي هذه الحالة، سيُتبَع اسم النطاق بسلسلةٍ نصيّةٍ واضحة تُحدِّد منشورًا معيّنًا، مثلًا: http://www.example.com/2016/01/09/my-new-post تغيير تركيبة الروابط الدائمة اذهب إلى: Settings > Permalinks (إعدادات > روابط دائمة) في لوحة التحكم للوصول إلى خيارات الروابط الدائمة، يمكنك الاختيار من أحد أكثر تركيبات الروابط الدائمة شيوعًا أو يمكنك إدخال تركيبة مُخصَّصة في حقل Custom Structure (تركيبة مخصّصة). هنالك خياراتٌ ستة لتنتقي منها: افتراضي: هذه هي الروابط الدائمة "القبيحة" اليوم + عنوان المقالة: تستعمل هذه التركيبة الصيغة "اليوم/الشهر/السنة" متبوعةً بعنوان المقالة؛ يُقصَد بعنوان المقالة هنا "الاسم اللطيف" (slug) للمنشور الشهر + عنوان المقالة: كما في الخيار السابق، لكن دون ذكر اليوم رقمي: يستعمل هذا الخيار مُعرِّف المقالة (post ID) من السطر الموافق لها من جدول wp_posts في قاعدة البيانات تركيبة مخصصة: يسمح لك هذا الحقل بتعريف تركيبة مخصصة تستطيع أن تستعمل فيها كل الوسوم البنيوية في ووردبريس الخيار الأول هو الخيار الافتراضي الذي يكون مُفعَّلًا بعد تثبيت ووردبريس. إنشاء روابط دائمة مخصصة توفر ووردبريس عشرة وسوم بنيوية لتعريف تركيبة خاصة بك. ستمر عليك أول سبعة من تلك الوسوم كثيرًا: %postname%: الاسم اللطيف (slug) للمنشور %post_id%: المُعرِّف الفريد للمنشور %category%: التصنيف الرئيسي للمنشور %monthnum%: الشهر الذي نُشِرَ فيه المنشور %day%: اليوم (بصيغة رقمية) الذي نُشِرَ فيه المنشور %author%: قد تستفيد منه في المواقع الشبيهة بالمجلات، التي فيها أكثر من كاتب إذا أردتَ أن تكون دقيقًا للغاية فيما يتعلق بالوقت في روابطك الدائمة، فيمكنك استعمال الوسوم %hour% و %minute% و %second%؛ لكن يصعب التفكير بمثال عملي تكون فيه الخيارات الثلاثة السابقة مفيدة. أبقِ في ذهنك أنَّه عليك أثناء تشكيل تركيبة مخصصة لروابطك الدائمة تضمين الوسم %postname% أو %post_id% لكي تستطيع تعيين منشور مُحدَّد؛ لأن هذين الوسمين هما الوحيدان اللذان يضمنان توليد مُعرِّفات فريدة. مع كل ما سبق من معلومات، لو أردت مثلًا أن تكون روابطك الدائمة محتويةً على مُعرِّفات المنشورات (post IDs) وأسمائها، فيمكنك استعمال الصيغة الآتية: ‎/%post_id%/%postname%/‎. من الجدير بالذكر أنَّك تستطيع تغيير تركيبة روابط التصنيفات والوسوم في موقعك في نفس المكان في لوحة التحكم في القسم "اختياري". الروابط الدائمة الملائمة لمحركات البحث و SEO الروابط الدائمة وبنية الروابط هما أمران مهمان من وجهة نظر محركات البحث، وعلى الرغم من وجود كمٍ كبيرٍ من المعلومات حول هذا الأمر على مر السنين، إلا أن توجيهات Google حول بنية URL تبقى واضحةً ومباشرة: أبقها –أي الروابط– بسيطةً قدر الإمكان وأن تكون مفهومةً للبشر. يُعطى المستخدمون في نتائج بحث Google أربعة أنواع من المعلومات: العنوان، والوصف، والتاريخ، والرابط الدائم. تُعطي هذه التفاصيل للمستخدم مؤشرًا عمّا إذا كانت تحتوي الصفحة ما يبحثون عنه. فمثلًا، لو كانت لديك مقالة عن الماعز الجبلي وتركت إعدادات الرابط الدائم الافتراضية، فسيبدو URL كالآتي: http://www.example.com/?p=135 أما لو فعّلتَ إظهار اسم المنشور في الروابط الدائمة، فسيكون URL كالآتي: http://www.example.com/mountain-goats/‎ الذي هو أسهل للقراءة والفهم. وبهذا نجد أنَّ الروابط الدائمة القبيحة لها تأثيرٌ سلبيٌ على SEO وليست صديقةً للمستخدم. أشارت مقالةٌ حديثةٌ في Moz.com حول هذا الموضوع إلى بعض الأمور الأخرى في هذا الخصوص، لكن الأساسيات ليست صعبة الفهم والتطبيق: أبقِ الروابط مختصرةً قدر الإمكان (ربما أقل من 100 محرف) استخدم الكلمات المفتاحية في الروابط في حدود المنطق، ولا تحاول حشرها أزل الخاصيات الديناميكية (التي تتغير مع مرور الوقت) من الروابط استعمل الشرطات (-) فواصلًا بين الكلمات، واحذف أدوات الربط مثل "and" و "or" و "but" و "of" وغير ذلك اختيار البنية المثالية للروابط الدائمة لموقعك لقد شرحنا إلى حد الآن الخيارات الأساسية لكيفية التحكم بالروابط الدائمة، لكننا لم نستوعب ما هي البنية المثالية للروابط الدائمة لموقع ووردبريس بعد. الإجابة المختصرة: الأمر نسبيٌ، إذ لن يستفيد كل موقع من نفس البنية، لكن هنالك بعض الأمور العامة لتأخذها بعين الاعتبار. أولها هو ضرورة تضمين اسم المنشور في الروابط الدائمة، حيث سيستفيد منها المستخدمون ومحركات البحث خير الاستفادة، وهذا غالبًا كل ما تحتاج فعله. أما لو كان موقعك إخباريًا، فربما تود تضمين معلومات التاريخ في الروابط الدائمة، وإلا فليس من المنطقي وضعها. إذا أردت تضمين معلومات التاريخ للقارئ، فمن الأفضل أن تضمنها في البيانات الوصفية للمنشور، مما يجعل التعرف عليها أسهل. تضمين معلومات عن التصنيفات في الروابط الدائمة هو أمرٌ معقولٌ إذا كان موقعك مُقسّمًا إلى أقسامٍ معيّنة، أبقِ في بالك أنك إذا كنتَ تستعمل أكثر من تصنيف لمنشورٍ ما، فسيُعرَض أحدها فقط في الرابط الدائم، وتكون الأولوية للتصنيف الذي يأتي أولًا بالترتيب الهجائي؛ أما إذا أردت تحكمًا كاملًا حول هذا الموضوع، فربما عليك استعمال إضافة "WP Category Permalink". مثالٌ على هذا النوع من الروابط الدائمة هو الروابط الدائمة في أكاديمية حسوب. لطالما كنت تُضمِّن عنوان المنشور في الروابط الدائمة، فلن يكون هنالك تأثيرٌ كبيرٌ لاختيارك أحد الأمور السابق ذكرها. كانت هنالك مشكلة في النسخ التي سبقت نسخة 3.3 من ووردبريس، وهي مشاكل في الأداء عند استخدام عنوان المنشور في الروابط الدائمة، لكن قد حُلَّت المشكلة لحسن الحظ. وكما أشار Matt Cutts، الرئيس السابق لفريق مكافحة spam في Google، سيكون اختيارك لبنية الروابط الدائمة متعمدةً على تفضيلك الشخصي بعد أن تُلم بالمعلومات الأساسية حولها. تفعيل الروابط الدائمة الجميلة اعتمادًا على خصوصيات الاستضافة عندك، ربما تحتاج إلى تفعيل بعض الإعدادات على الخادوم لكي تعمل الروابط الدائمة عملًا صحيحًا. بأسلوبٍ مبسط: يجب أن يملك خادوم الويب عندك طريقةً ما لتحويل الروابط الدائمة إلى شيءٍ يستطيع تنفيذه؛ وهذا يختلف بناءً على نوع خادوم الويب الذي تستعمله. لاحظ أنَّ أغلبية موفري الاستضافة يهتمون بهذه التفاصيل ولا داع لخوضك فيها، لكن ربما تجد نفسك مضطرًا إلى ذلك بين الحين والآخر. سنشرح أشهر الطرق لإعداد الخواديم، لكن إن وجدت نفسك تمر بمأزقٍ ما، فانظر إلى صفحة Fixing Permalink Problems في دليل مطوري ووردبريس المسمى Codex. استعمال الروابط الدائمة في أباتشي عادةً ما يكون خادوم الويب المستعمل لأغلبية مواقع ووردبريس هو أباتشي (Apache)، إن كان هذا هو الحال عندك، فعليك أن تتأكد من وجود بعض المتطلبات الأولية، ومن صحة ضبطها. أولًا، يجب تثبيت وتفعيل واحدة mod_rewrite في أباتشي؛ ويلزمك في تفعيل خيار FollowSymLinks في ضبط المجلد الذي يحتوي ووردبريس، وأن يُسمَح بخيار FileInfo. يجب أن يكون هنالك ملف ‎.htaccess تستطيع ووردبريس استعماله، وإن لم يكن موجودًا فستحاول ووردبريس إنشاءه عندما تُفعِّل الروابط الدائمة "الجميلة" ويجب أن تملك ووردبريس امتيازات الكتابة عليه أيضًا. محاولة تعديل وإصلاح ملف ‎.htaccess هو أمرٌ متعبٌ وشاق، لذلك لا ننصحك بذلك إلا إن كانت لك خبرةٌ سابقةٌ عن هذا الموضوع. يمكنك العثور على معلومات تفصيلية حول كيفية تنصيب وإعداد خادوم ويب أباتشي في الدروس المتعلقة بأباتشي في أكاديمية حسوب. بفرض أنَّ ضبط ما سبق صحيح، فيجب أن تتمكن من إدارة خصائص الروابط الدائمة من لوحة تحكم ووردبريس. استخدام الروابط الدائمة في خواديم الويب الأخرى أباتشي ليس خادوم الويب الوحيد الذي شاع استعماله، هنالك بدلاءٌ عنه مثل Nginx و Lighttpd. يمكنك العثور على معلومات حول تنصيب وإعداد خادوم ويب Nginx. تغيير تركيبة الروابط الدائمة في موقع حي من الناحية المثالية، يجب أن تُقرِّر ما هي تركيبة الروابط الدائمة التي تُفضِّلها قبل إطلاقك للموقع، ثم لا تغيرها بعد ذلك. لكن ربما ستجري بعض التعديلات على الموقع في مرحلةٍ ما. أبقِ في بالك أنَّ هذه الخطوة لها وقعٌ كبيرٌ إن أجريتها على كامل الموقع، وأنت تخاطر باللعب بالنار عندما يصل الأمر إلى تقييمات SEO والروابط الخارجية التي تُشير إلى صفحات موقعك. عمل التغيرات في لوحة تحكم ووردبريس هو أمرٌ بسيطٌ، لكن عليك أنَّ تستعمل إعادة التوجيه (301) لكل الروابط القديمة للتأكد أنَّك لن تزعج المستخدمين ومحركات البحث على حدٍ سواء؛ ابدأ بجمع قائمة كاملة لكل الروابط السابقة، وما هي الروابط الجديدة التي يجب أن يُعيدوا التوجيه إليها. ربما تستعمل إضافة مثل Redirection أو SEO Redirection للتأكد من أنَّ الروابط القديمة ستُشير إلى المقالات بشكلٍ صحيح. الخلاصة تقرير ما هي بنية الروابط الدائمة هو من أول القرارات التي عليك أن تتخذها عند إنشاء موقع ووردبريس جديد. من وجهة نظر SEO: من الأفضل عدم استخدام الضبط الافتراضي "القبيح" وإنما يجب استعمال الروابط الدائمة "الجميلة" لأنها قابلة للفهم من البشر، ومن المهم أخذ قواعد Google للروابط بعين الاعتبار عند ضبط "الأسماء اللطيفة" (slugs) لصفحات ومقالات موقعك. في النهاية، إن قررت تغيير بنية الروابط الدائمة في موقعٍ حي، فافعل ذلك بقدرٍ كبيرٍ من الحذر وتأكد أنك تستعمل أدوات مناسبة للتعامل مع إعادة التوجيه. إن كانت لديك أيّة أسئلة أو استفسارات حول الروابط الدائمة، فاترك تعليقًا وسنحاول جاهدين مساعدتك. ترجمة -وبتصرّف- للمقال The Ultimate Guide to WordPress Permalinks لصاحبه Tom Ewer.
  5. مخطط الفئات (classes) هو جزءٌ مهمٌ جدًا من لغة النمذجة الموحدة UML، وهو مخطط هيكلي مهمته عرض الفئات بنظامٍ معيّن مع جميع العلاقات التي تربط بينها، وهو -برأيي- أشهر نوع من المخططات في هندسة البرمجيات. يساعدك رسم مخطط الفئات على رؤية المشكلة بأفقٍ أوسع؛ وعندما تكتبها، فستفرِّغ مساحةً في رأسك للأفكار الجديدة. وتُسهِّل أيضًا فهم هيكلية الفئات من الآخرين عندما تناقش المشكلة معهم. الفكرة هي أنني أنسى عادةً بنية المخططات عندما أحاول قراءة أحد المخططات التي رسمها غيري، فلهذا قررت كتابة هذه المقالة لعلها تذكرني بها في المستقبل. دورة علوم الحاسوب دورة تدريبية متكاملة تضعك على بوابة الاحتراف في تعلم أساسيات البرمجة وعلوم الحاسوب اشترك الآن الفئة – Class المكون الأساسي لهذه المخططات هو مخطط الفئة، التي تَظهَر على شكل عقدة (node) وعادةً كصناديق (boxes)، يمكن أن يُعرَّف لكل صنف دوال (methods) وخاصيات (attributes)، كما هو موضَّح بالشكل أدناه: الوراثة – Inheritance وراثة الفئات -في ما يتعلق بمخططات UML- هي علاقة تعميم (generalization) التي تمثل علاقة "هو" (is a) على مستوى الفئة، المخطط الآتي يُظهِر كيفية رسم التعميم. التطبيق – Realization هنالك علاقة مختلفة في UML للواجهات (interfaces)، فالوراثة من واجهة تسمى "implementation" التي هي علاقة "تطبيق" (realization) في مخططات UML. تمثيلها الشكلي مشابه للوراثة، إلا أنَّ الخط مقطّع (dashed)، ويجب تحديد أنَّ الواجهة هي «مجرَّدة» (abstract) – أي أنَّ اسمها مكتوبٌ بخطٍ مائل؛ كما هو مبيّن في هذا الرسم. الارتباط – Association شكل آخر من أشكال العلاقات في مخططات الفئات هو الارتباط (association)، وهو علاقة على مستوى الكائنات (object-level) أي أنه يحدث بين كائناتٍ لأصنافٍ مرتبطةٍ؛ لذا تُمثَّل كل العلاقة كعائلة من الوصلات (links). هنالك عدة أنواع من الارتباط مُحدَّدة أكثر (التجميع aggregation و التألف composition). التجميع – Aggregation التجميع هو شكل أكثر تحديدًا وتخصيصًا من الارتباط. وهو علاقة "لديه" (has a)؛ التمثيل الرسومي لهذه العلاقة هو الآتي: التألف – Composition شكل أكثر تخصيصًا من التجميع هو التألف (composition) فبدلًا من علاقة "لديه" (has a) تكون العلاقة هي "يملك" (owns a). وهذا ملائمٌ للعلاقات التي لا يمكن أن يتواجد فيها كائن إلا كجزءٍ من كائنٍ آخر. على سبيل المثال، إن كنت هنالك طائرة تملك جناحًا فهذا تألف، فماذا ستفعل بالجناح لوحده؟ لكن إن كانت هنالك بركة فيها بعض البط فهذا تجميع، لأنه يملك للبط أن يعيش دون بركة (وإن لم يكن سعيدًا بذلك)، والبركة ستبقى بركة حتى لو لم يكن فيها بط؛ التمثيل الرسومي لعملية التألف هو مثل التجميع، لكن المُعيَّن مملوء وليس مُفرَّغ. الاعتمادية – Dependency آخر نوع من العلاقات هو "الاعتمادية" (dependency) وهو أضعف من الارتباط (association) ويقول: إن كانت الفئة تستعمل فئةً أخرى، فهي تعمد عليها. يكون من المناسب استعمالها في حالات تكون نسخةٌ من الفئةِ مخزنةً في متغيرٍ محلي في دالة فئة أخرى، أو إذا استعمِلَت دالةٌ ثابتةٌ (static method)؛ لذا لن تكون الفئات مرتبطةً، لكن واحدة تعتمد على الأخرى. خلاصة صممت كل الأمثلة السابقة باستخدام محرر مفتوح المصدر باسم Dia، وأنا أنصح باستخدامه. ولأنه محرر رائع، فهذه صورة شاملة لجميع أنواع العلاقات إن أحببت طباعتها. ترجمة -وبتصرّف- للمقال UML Class Diagram لصاحبه Radek Pazdera.
  6. هل لدى مشروعك مجتمعٌ نشطٌ، لكن هنالك الكثير من العمل الذي لا يستطيع ذاك المجتمع تحمل عبئه؟ هذه آخر مقالة من سلسلة المقالات حول التسويق للمشاريع مفتوحة المصدر، وقد حان الوقت للتركيز حول كيفية تنمية المجتمع. الدروس السابقة في هذه السلسلة: الجمهور المستهدف وصفحة الهبوط كيف تجعل الوصول مشروعك المفتوح المصدر أسهل طرق التعريف بمشروعك المفتوح المصدر كيفية تحويل مستخدمي مشروعك مفتوح المصدر إلى مساهمين كيف تنمي المجتمع الخاص بمشروعك مفتوح المصدر (هذا الدرس) السبب وراء تنمية المجتمع إن بدَأ مجتمعك بالنشوء بنجاح، لكنه لم ينمو كما كنتَ متوقعًا، فربما هنالك العديد من الأسباب المُمكنة لذلك، وعليك معرفتها ولتكن واقعيًا حولها. ستنتمي تلك المسببات إلى أحد هذين التصنيفين: الأشياء التي يمكنك فعل شيءٍ حيالها الأشياء خارج نطاق قدرتك هل أصبح متتبع العلل issue tracker لديك مزدحمًا بعد ازدياد عدد الأشخاص العاملين عليه؟ ألم يزدد وقت استجابتك لاستفسارات المساهمين الجدد لأنك تقضي وقتًا أطول لمناقشة الأمور مع الأشخاص الموجودين ضمن مجتمعك؟ هل تبقي التوثيق محدثًا دومًا؟ هل مضت سنتان منذ آخر مرة نشرتَ فيها تدوينةً حول المشروع؟ ليس من الصعب نسيان هذه الأمور الصغيرة عندما يصبح المجتمع الخاص بمشروعك كبيرًا ولا يتوقف عن الحركة والعمل ويتطلب منك اهتمامًا دائمًا. لكن نسيان تلك الأمور قد تسبب توقف نمو مشروعك في المستقبل؛ فكلما كبر مشروعك، كلما أصبح معقدًا بعمليات ونقاشات كثيرة يجب على القادمين الجدد أن يعتادوا عليها، مما يؤدي إلى تصعيب انضمام المساهمين من جديد. إن كانت المسببات من التصنيف الثاني، فهذا مُشكل كبيرٍ لك، خصيصًا للمشاريع الصغيرة، فقد لا يكون المجتمع كبيرًا ليحتوي عددًا كافيًا من المساهمين؛ فربما هنالك عشرة أشخاص في هذه الكوكب مهتمين بمشروعك وقادرين على المساهمة فيه، وخمسة منهم يساهمون فيه من قبل، وثلاثة لديهم عقود عمل تجعل من الصعب عليهم المساهمة في المشاريع، واثنين آخرين يفضلون قضاء وقت فراغهم مع أولادهم الصغار. يتطور عالم التقنية بوتيرة عالية، ألم تلاحظ عدم وجود نشاط كبير في مكتبة COBOL التي كتبتَها في السنوات القليلة الماضية؟ ربما تقوم بكل شيءٍ بشكلٍ صحيح، لكن الآخرين ينتقلون إلى أشياءٍ جديدة. فعندما بدأتَ كنتَ تستبدل تقنيات قديمة بهذه المكتبة الجديدة، وهذا ما يحدث اليوم لمكتبتك. الخيارٌ عائدٌ إليك إن كنتَ تريد متابعة العمل على ما هو قديم، أم "القفز" من السفينة كما يفعل الباقون. بعد أن شرحنا المشاكل المحتملة، سنعرض كيفية حلها. حسن باستمرار أمعن النظر بمدى سهولة الانضمام إلى المشروع للوافدين الجدد، وجرِّب سلوكياتٍ وتقنياتٍ مختلفة لترى أيها أصلح لك. يتطلب الحفاظ على مجتمعك وتوسعته جهدًا كبيرًا كما هو الحال عند بناءه، لكنك لست وحدك هذه المرة. اجعل بعض العادات الجيدة في إدارة المجتمع جزءًا من خطة سير العمل. هل سبب أحدهم مشكلة في الواجهة؟ عليهم أيضًا تحسين التوثيق. شجِّع الذين أضافوا ميزات كبيرة أن يكتبوا تدوينةً عنها؛ فهذا عملهم وعليهم أن يفخروا به. اشرح كل ما تفعله في سلسلة من التدوينات على صفحة مشروعك، كي يتطلع الآخرون إلى مساعدتك؛ شاور مجتمعك في أمور المشروع، واعطِ الأعضاء الموثوقين امتيازات وصول كاملة إلى المستودع؛ وبهذا ستصنع "آلة" ذات اكتفاءٍ ذاتي تستطيع أن تعمل دون استهلاك كامل طاقتك ووقتك الفارغ. اعرض مشروعك في أحد المؤتمرات هنالك الكثير من المؤتمرات التي تُركِّز على مختلف جوانب الحواسيب حول العالم. إن أردت أن يظهر مشروعك أمام العالم، فسجِّل كي تلقي كلمةً حوله في أحد المؤتمرات؛ وستتاح لك فرصٌ عديدةً إن لم يكن منتجك تجاريًا. يمكن أيضًا عرضه على الأشخاص الذين قد يرونه مفيدًا في اللقاءات والاجتماعات المحلية المختصة بتقنية المعلومات. أحد أكبر المؤتمرات هو FOSDEM في بروكسل، سجل مشروعك لمؤتمر السنة القادمة، حتى لو كنت ستتحدث عنه للتعريف به فقط (وليس لشرح تفاصيله). اعمل مع المطورين الأقل خبرة تتوجه معظم النصائح التي قدمناها في هذه السلسلة إلى الأشخاص الذين لديهم باع طويل في مجالاتهم، أي أنهم مطورون أولي خبرةٍ متوسطة إلى متقدمة، والذين يبرمجون لفترةٍ لا بأس بها. لكن المشكلة أن لديهم عملًا بدوامٍ كامل، وربما لديهم عائلة ومشاريع فرعية عليهم العمل عليها. خصص جزءًا من وقتك كي تسهل على المساهمين الأقل خبرةً أن يعملوا على مشروعك، وهذا يساعدك في كسب مجموعة كبيرة من الأناس المتحمسين الذين يودون الحصول على الخبرة بالعمل معك؛ وهذا يتضمن الطلاب، والأشخاص الذين يغيرون مهنتهم... إلخ. ضع بذهنك أنهم لن يبقوا قليلي الخبرة إلى الأبد، فقد تحصل على أعضاءٍ رائعين في مجتمعك في المستقبل إن أقمت علاقاتٍ بناءةٍ معهم. ادفع مالا للمساهمين لن يكون هذا الخيار واقعيًا للمشاريع التي يقوم عليها أشخاص، لكنه فعالٌ جدًا للشركات. إن استعملت مواقع مثل BountySource، فيمكنك تحديد سعر للميزات التي تريد إضافتها وربما يأتي أحدهم ويقبل عرضك. خيارٌ أخرٌ هو إنشاء مسابقات حيث يعمل فيها الآخرون على مشروعك لقاء مبلغٍ مالي أو شكل من أشكال التقدير الشخصي. تختار العديد من الشركات تمويل الأشخاص المهمين في المجتمع وتدفع راتب عملٍ بدوامٍ كامل لهم ليعملوا على المشروع. هذا مثالٌ ممتاز يبيّن كيف أنَّ العمل على المشاريع مفتوحة المصدر لا يعني أنَّه بالمجان. خاتمة فتح مصدر مشروعك هو بداية جيدة، لكن ستزداد المتعة عندما يبدأ الآخرون باستعماله والمشاركة فيه؛ وحتى ولو كان أغلبنا مبرمجون، فإننا نجد أنَّ المصدر المفتوح أشبه "بمؤسسة اجتماعية". هذه آخر مقالة من سلسلة "التسويق للمشاريع مفتوحة المصدر"، شكرًا لبقائك معنا حتى النهاية، أرجو أن تكون قد وجدتها مفيدةً :-) . ترجمة -وبتصرّف- للمقال Growing the community around your open-source project لصاحبه Radek Pazdera.
  7. يجعل المساهمون من المشاريع مفتوحة المصدر مرحةً، فالسماح للآخرين باستعمال، وإعادة استعمال، وتحسين الشيفرات الخاصة بك وتحويلها إلى شيءٍ جديد لم تكن تتخيله، هو أمرٌ رائع؛ لكن -كغيره من الأمور في هذه الحياة- لن يتشكل المجتمع من العدم. ونحن نمر في هذه السلسلة من المقالات بعدة استراتيجيات لمساعدتك في بناء مجتمع حول مشروعك المفتوح المصدر؛ وركَّزت آخر ثلاث مقالات على جذب المستخدمين، وسنلقِ نظرةً هذه المرة على كيفية تحويلهم إلى مساهمين. الدروس السابقة في هذه السلسلة: الجمهور المستهدف وصفحة الهبوط كيف تجعل الوصول إلى مشروعك مفتوح المصدر أسهل التعريف بمشروعك مفتوح المصدر كيف تحول مستخدمي مشروعك مفتوح المصدر إلى مساهمين (هذا الدرس) كيف تنمي المجتمع الخاص بمشروعك مفتوح المصدر وضح أن تطبيقك مفتوح المصدر إن جعلت تطبيقك سهل التثبيت بإنشاء مُثبِّت أو حزمة، فهنالك احتمالٌ كبيرٌ ألّا يفتح المستخدمون صفحة الهبوط أو المستودع؛ ولا بأس في ذلك في أغلبية الأوقات، لكن عليك أن تضع أنَّ تطبيقك مفتوح المصدر في مكان ما فيه لأولائك الذين يريدون التواصل ومعرفة المزيد من المعلومات حول تطبيقك. قد تستعمل خيارًا في سطر الأوامر، أو مربع حوار "حول" أو "About" إن كان لتطبيقك واجهةٌ رسوميةٌ؛ وضع رابطًا لصفحة الهبوط وربما لمتتبع العِلل الخاص بك (المكان الذي يُبلِّغ الناس فيه عن المشكلات التي تواجههم). ومن المحتمل أنَّ نكتةً في التطبيق ستدفع المستخدم إلى زيارة الرابط الموجود فيها. مكانٌ جيدٌ لتضع فيه طرق التواصل معك هو عبر رسائل الخطأ ومربعات الحوار التي تظهر عند انهيار التطبيق؛ أخبر فيها المستخدمين كيف يبلغون عن المشكلة، وهذه هي أكثر الطرق شيوعًا للمشاركة في تطوير المشاريع؛ حيث يحاولون فعل شيءٍ ما، لكنه لا يعمل وسيمدون يد العون في إصلاح المشكلة. المزايا المرتقبة إحدى طرق دفع الآخرين للمشاركة هي استعمال الميزات غير المكتملة؛ ربما تدمج الميزات التي تود إضافتها للتطبيق في المستقبل إلى واجهة المستخدم وتُظهِر رسالة تشرح أن هذه الميزة لم تتم، وتدعو الآخرين إلى مساعدتك في إكمال العمل عليها. قد تستعمل خيارًا إضافيًا في سطر الأوامر الذي يعرض رسالةً (بدلًا من الميزة المطلوبة)، ويمكن فعل المثل بمربعات الحوار في التطبيقات الرسومية. ضع ببالك أن هذا قد يصبح مزعجًا بشكل سريع؛ ولهذا عليك استعماله مع الميزات غير الأساسية في مشروعك؛ فلن يُسعَد مستخدموك إن نزَّلوا مكتبتك الجديدة عن الفرز (sort) ليجدوا أنَّ الفرز السريع (quick sort) ليس مُبرمجًا بعد. اكتب توثيقا منفصلا للمطورين يجب أن يكون لمشروعك توثيقٌ من نوعٌ ما للمطورين مع لمحة من مكان تواجد الأجزاء الرئيسية من البرنامج، ومرجع للواجهات البرمجية (APIs) يمكن البحث فيه. من الأفضل أن يتم توليد ذاك التوثيق من الشيفرات المصدرية بأدواتٍ مثل doxygen، أو JSDoc، أو YARD. يساعدك وجود التوثيق مع الشيفرات بتسهيل التعديلات البرمجية على المشروع. وهنالك مواقع مثل RubyDoc يسمح لك بتوليد واستضافة التوثيق مجانًا. لا يوجد أبسط من هذا! إن كتبت لمحة للمطورين عن المشروع وأبقيتها مختصرة ومحددة، فليس من المحتمل أن تحتاج إلى إعادة كتابتها بين الحين والآخر، إلا إن كنت تعيد هيكلة المشروع بأكمله. وضح ما الذي يجب فعله من المحتمل أن يتمحور تطوير تطبيقك حول مُتتبع العلل، فستأتي العلل وسيحاول المساهمون تتبعها وحلها، وفي حال استمرت هذه العملية، فسيمسي من السهل أن يتحول إلى كومة مربكة من الأشياء التي لن يستطيع أحد غيرك أنت وبعض أفراد فريق المساهمين فهم طريقة تنظيمها. لكن كيف سيتمكن المساهمون الجدد من البدء في العمل؟ حاول أن يكون متتبع العلل منظمًا بشكلٍ جيد وكأنك ستتجه غدًا إلى العمل مزارعًا في الحقل، ولن تدخل إلى الإنترنت بعد ذاك اليوم أبدًا، وسيأتي أحدهم ليكمل ما بدأتَه. أبقِ قائمةً بالمهام البسيطة والمحددة للمساهمين الجدد ليفعلوها عندما يأتون لأول مرة، حتى لو كنت تستطيع تنفيذها أنت بخمس دقائق، ربما يأخذ تفصيلُ شرحِ مشكلةٍ ما في متتبع العلل وقتًا لا بأس به، لكنه سيؤتي أكله إن ساعدتْ تلك العلة بتثبيت مساهم جديد في المشروع. كن متجاوبا عندما يحصل مشروعك على أول المستخدمين أو حتى أول المساهمين، فستبدأ باستقبال أسئلة، وطلبات للميزات، وتبليغات عن العلل وحتى سيصلك حلولٌ لتلك العلل؛ حاول أن تستجيب وتساعد الآخرين بمشاكلهم في إطار زمني محدد، حتى لو كنت مشغولًا وتقوم بتطوعك في وقت فراغك. فستؤدي استجابتك لطلبات pull requests بعد شهر إلى فقدان المساهمين لحماسهم. أجب حتى وإن لم تستطع إنجاز ما طُلِبَ مباشرةً، فمن الأفضل أن تقول "لا" وتضع الطلب في قائمة الأعمال غير المنجزة من أن تتركه معلقًا في الهواء أو أن تعد بشيءٍ لا تستطيع تلبيته. لكن بدلًا من ذلك، ضع الطلب في متتبع العلل لعل أحدهم ينجزه؛ هذا هو جمال البرمجيات مفتوحة المصدر. أصبح مساهما إن كنت حديث العهد بهذا العالم المُربك من البرمجيات الحرة والمفتوحة المصدر، أو ربما كان مشروعك خارج "البيئة" التي نشأت بها واستخدمتها من قبل، فجرب المساهمة في مشاريع مشابهة وانظر إن كان من السهل البدء من التوثيق الذي كتبوه، وأين بدأت تواجه مشاكل معه. هل أجاب أحدهم عن تساؤلك أو قَبِلَ الرقعة (patch) التي أرسلتها؟ استخدم خبرتك من هذه التجربة لتقديم مشروعك بأفضل صورة ممكنة لتسهل انضمام المساهمين الجدد. ما التالي؟ لقد أرسيتَ أساسات مشروعك، وأعداد مستخدميه والمساهمين فيه تزداد باطراد، فما الخطوة الآتية؟ هذا ما سيكون موضوع آخر المقالات في هذه السلسلة. ترجمة -وبتصرّف- للمقال Turning users into contributors لصاحبه Radek Pazdera.
  8. حتى لو لم تكن تطلب من الأشخاص مالًا لقاء استعمالهم لتطبيقك، لكن ما يزال علينا التسويق قليلًا له قبل أن يسطع نجمه. كيف إذًا سيعلم الآخرون عنه؟! الناس مشغولون هذه الأيام التي أصبحت فيها المشاريع مفتوحة المصدر لا تُعد ولا تحصى، وأصبحت المنافسة قوية جدًا على المساهمين في المشاريع. عرفنا -في آخر مقالين من هذه السلسلة- الجمهور الهدف لمشرعنا ثم تأكدنا أنه متاحٌ -وبسهولة- لمن شاء أن يستعمله؛ هذا الدرس هو القسم الثالث من سلسلة التسويق للمشاريع مفتوحة المصدر وسيركِّز على كيفية التعريف بمشروعك. أقسام هذه السلسلة: الجمهور المستهدف وصفحة الهبوط كيف تجعل الوصول إلى مشروعك مفتوح المصدر أسهل طرق التعريف بمشروعك مفتوح المصدر (هذا الدرس) كيف تحول مستخدمي مشروعك مفتوح المصدر إلى مساهمين كيف تنمي المجتمع الخاص بمشروعك مفتوح المصدر الفكرة بسيطةٌ للغاية: عليك أن تعرض المشروع على الأشخاص المناسبين في الوقت الملائم (عندما يحتاجون إلى التعرف على مشروعك)؛ كل برنامج له وظائف معيّنة تجعله مفيدًا لمجموعة بسيطة من المستخدمين. لا ترسل رسائل عشوائية لأكبر عدد تستطيعه من الأشخاص؛ لكن وجِّه جهودك إلى المجتمعات التي ستستفيد من المعرفة عن مشروعك. لنقل أنَّك برمجت إطار عملٍ مثل ExpressJS، عمومًا ستكون جهودك موجهة نحو الأشخاص الذين يطورون مواقع الويب؛ وربما تود أن تستهدف أولاءك الذين "يفكرون" في صنع موقع إلكتروني لكن لم يبدؤوا بعد، أما البقية فسيقولون "هذا رائع!" لكنهم سيواصلون استعمال Sinatra أو Flask لأن الشيفرات التي كتبوها تعتمد عليها. حل المشكلات وهذا ما يبرع المبرمجون بفعله. لِمَ لا تستعمل ذلك لصالحك؟ لقد أنشأت مشروعك لحل مشكلة واجهتك ومن المحتمل أنَّ الآخرين يحاولون حل نفس المشكلة. أنشِئ محتوى حول المشاكل التي يحلها مشروعك، وما الذي يفعله بشكلٍ جيد. وهذا يعني أنَّ عليك كتابة مقالات، أو صنع مقاطع فيديو، أو تسجيل بودكاست، أو حتى إلقاء محاضرات أو التعليم في دورات تدريبية، أيها تُفضِّل. لكن النص يبقى هو الأفضل لأنه دائم وقابل للبحث. يمكنك نشر تلك المواد في عددٍ من الأماكن، مثل مدونتك الشخصية أو أن تنشر منشورًا على مدونة خاصة بالمجتمع التقني الذي ينتمي إليه مشروعك. ثم شارك ذاك المقال مع مَن تظن أنهم سيجدونه مفيدًا. وهذا يتضمن منتديات النقاش، والمجموعات البريدية، وقنوات Gitter، و Reddit أو Hacker News. تأكد أنَّ ذاك المجتمع سيرحب بذلك (بعض الأقسام الفرعية من Reddit [يسمونها subreddits]) لا تسمح بنشر روابط)، وكن حذرًا جدًا بالتعامل مع القوائم البريدية، فراعي أنَّ رسالتك ستصل إلى البريد الوارد لكثيرٍ من الأشخاص. إن كانت هنالك العديد من قنوات التواصل لجمهورك المستهدف، فلا تُرسِل الرسائل إليها جميعًا، اختر قناتين أو أكثر منهم فقط، وإن أُعجِبَ الجمهور بمشروعك فسيتنشر تلقائيًا. اجعل تواتر نشرك للمقالات في حدود المعقول وعدِّلها بناءً على ردة فعل القراء، فإن أعجبتهم، فذاك أمرٌ حسن، وأتبعها أخرى في الأسبوع القادم؛ وإن لم يكونوا متحمسين، فحاول شيئًا جديدًا في المرة القادمة، وستكون الردود على مشروعك أفضل إن كنت تشارك في المجتمع مشاركةً فعالةً (بجانب مشاركتك لدعايتك إلى مشروعك). الإجابة عن الأسئلة إن كان مشروعك يحل مشكلة مخصصة جدًا، فربما من المفيد أن تراقب المواقع التي يسأل الناس فيها عادةً عنها، مثل StackOverflow أو Reddit، وأن تساعدهم في الأمر. وعندما تُجيب، حاول دائمًا أن تسهب في الموضوع قليلًا لإعطاء مجال لتشرح كيف يعمل حلّك للمشكلة، ولا بأس أن تُعدِّد الخيارات الأخرى المتاحة. ربما ذلك يستهلك وقتًا أكثر من إعطاء مجموعة روابط، لكن ربما تستفيد من سؤالك بتحويله إلى تدوينة لاحقًا. تلك المواقع هي مولدات أفكار رائعة؛ فإن أردت أن تعرف ما هي المشاكل التي يعاني منها الناس، فألقِ نظرةً على الأسئلة التي يسألونها. العمل مع الآخرين لا يتواجد مشروعك في الفراغ، وإنما بُنِيَ باستعمال بعض المكتبات الأخرى، وهنالك أشياءٌ أخرى يمكن أن تُدمَج أو تتعاون معه. أخبر الناس كيف يستطيعون استعمال مكتباتك وأرهم كيف يدمجون مشروعك مع الأدوات الأخرى. قدِّم ما لديك لأعضاء المجتمعات التي تلتف حول تلك المشاريع، وسينظرون إليها إن وجدوها مفيدةً؛ وعلى الرغم من أنك تعتمد على شعبية مشروع قائم، إلا أنك تساهم في نشره في نفس الوقت. وما لم يثر المحتوى الذي تقدم جدلًا، فلا يوجد سببٌ يمنع أصحاب تلك المشاريع من الترحيب لكتابتك عن مشاريعهم، وربما يشاركون ما كتبتَ أيضًا: "رائع! أحدهم أنشأت روبوتًا باستخدام مكتبتي". كن لطيفا لا تقلل من شأن عمل غيرك لكي تعلي من شأن عملك. ربما تظن أن مشروعك هو الأفضل، صحيح؟ هذا لأنك من أنشأه وقد يكون هذا صحيحًا، ولا بأس من عمل مقارنات، لكن لا تقل أشياءً مثل "على عكس مكتبة RottenWatermelon JS، هذا الإطار يحتوي على sane API" أو "اضطررت على مر السنين أن أتعامل مع الخردة المسماة ‎OMG++‎، لكنني أتيت اليوم لأفتح مصدر اللغة Gobbledygook: لغة البرمجة للقرن الحادي والعشرين". قد يلفت الجدار النظر إليك، لكنه لن يُنشِئ بيئةً صحية وودودةً لكي ينمو بها المجتمع المحيط بمشروعك؛ تذكر أنه في خمس سنين سيصبح مشروعك تقنيةً قديمةً جدًا ولن يستعملها أحد. ما التالي؟ أصبح مشروعك موجودًا وأصبح يحصل على أول مستخدميه؛ عليك الاستمرار في جهودك وسيؤتي عملك أُكله، سننظر في المقالة التالية كيف نحول المستخدمين إلى مساهمين. ترجمة -وبتصرّف- للمقال Spreading the word about your open-source project لصاحبه Radek Pazdera.
  9. أصبحت المشاريع مفتوحة المصدر شيئًا عظيمًا؛ لكن بالتزايد الكبير لأعداد المشاريع مفتوحة المصدر، ازدادت صعوبة جذب المساهمين. وتدعم الشركات الكبرى -مثل فيسبوك و تويتر- عددًا كبيرًا من تلك المشاريع، مما جعل الجميع يتنافس لنيل مساهمات المُبرمجين ولفت انتباههم. هذا ثاني درس في هذه السلسلة من المقالات التي سترشدك إلى كيفية بناء مجتمع حول مشروعك. الجمهور المستهدف وصفحة الهبوط كيف تجعل الوصول إلى مشروعك مفتوح المصدر أسهل (هذا الدرس) طرق التعريف بمشروعك مفتوح المصدر كيف تحول مستخدمي مشروعك مفتوح المصدر إلى مساهمين كيف تنمي المجتمع الخاص بمشروعك مفتوح المصدر تحدث الدرس السابق عن الفئة المستهدفة وكيف تُقدِّم مشروعك إليهم؛ وفي هذه المرة سننظر إلى جعل الوصول إليه سهلًا. عندما يرى المستخدم أنَّ مشروعك يفي باحتياجاته، فسيبحث عن تعليمات حول كيفية استعماله وطريقة عمله؛ ومن المفيد لك أن تجعل عملية التثبيت أسهل ما يمكن لتجنب خسارة الأشخاص على الطريق، وهذه هي طريقة فعل ذلك: ابن حزمة يأتي المشروع الاعتيادي مع مجموعة من الخطوات لبناءه وتثبيته، لكن من الأفضل اختصار تلك المجموعة إلى خطوة وحيدة: تثبيت حزمة. هل ستجرب مشروعًا يحتاج تثبيته إلى أمر sudo apt-get install وحيد، أم ستجرب مشروعًا يحتاج إلى إعداد بيئة لبناء البرامج، وتثبيت عدد هائل من الاعتماديات، ثم البناء من المصدر؟ حزم التوزيعات أول خيار أمامك هو إنشاء حزمة خاصة بنظام التشغيل، ربما لتوزيعة لينُكس (حزم deb، أو rpm، أو pacman) أو لنظام OS X باستعمال Homebrew. أو تستطيع إنشاء حزمة على مستوى اللغة البرمجية (language-level) إن كان هذا الخيار متوفرًا للتقنية التي بنيت مشروعك بها. الحزم الموجودة في مستودعات التوزيعات هي أكثر الحلول راحةً للمستخدم، سامحةً لهم بتثبيت البرمجيات التي تُوفرها من مصدرٍ موثوق. عملية تعلم إضافة حزمة مشروعك إلى المستودعات هي عمليةٌ مرهقةٌ وصعبة وعليك المرور على عشرات الصفحات من التوثيق حتى تُقبَل حزمتك في المستودعات؛ لكن ذلك العمل سيؤتي أُكله، فسيصبح تثبيت مشروعك يتطلب تنفيذ أمرٍ وحيدٍ من المستخدم؛ وذلك يستحق كل هذا الجهد لوجود الملايين من مستخدمي نفس نظام التشغيل. الحزم على مستوى اللغة أو يمكنك إنشاء حزمة على مستوى اللغة البرمجية -إن كان هذا الخيار متوفرًا للتقنية التي تستعملها- مثل PyPI، و Rubygems، و npm وغيرها. وإنشاء تلك الحزم أبسط، وتُخَلِّصُكَ من قراءة عشرات الصفحات من التوثيق التي عليك المرور بها كي تُقبَل حزمتك في مستودعات توزيعةٍ ما. وقد تكون هذه الطريقة أفضل بناءً على فئتك المستهدفة. إنشاء حزمة على مستوى اللغة هي ضرورة مُلِحَّة للمشاريع التي ستُستعمَل من بقية المطورين مثل المكتبات. فلمطوري روبي أو JS، لن تكون مكتبةٌ ما خيارهم المفضَّل إن لم يكن لديها حزمة gem أو npm. سينتشر مشروعك أكثر عندما تضعه في تلك المستودعات، إذ سيظهر عندما يبحث المبرمجون في تلك المستودعات عن المكتبات المفيدة. ربما يكون هنالك -في بعض الأحيان- أدوات أو خوارزميات لتحويل حزمة على مستوى اللغة إلى حزمة نظام، وبهذا تستطيع أخذ حزمة مشروعك على مستوى اللغة وإعادة استعمالها في توزيعاتٍ عدِّة؛ فيتمكن Debhelper -مثلًا- من بناء تطبيق بايثون تلقائيًا إن كان يأتي مع ملف setup.py. سكربت إعداد تلقائي إن لم تسطع توفير حزمة لمشروعك، فعليك -على الأقل- أن توفِّر سكربت لبناء وتثبيت البرمجية. وهذا السكربت قد يمثل بديلًا للحزمة لأنه يوفر طريقةً سهلةً للتثبيت. هذه بعض المشاريع الأخرى الشهيرة التي تستعمل سكربتات الإعداد: oh-my-zsh rvm لا يثق بعض الأشخاص في تلك السكربتات، وهم على حق في بعض الأوقات. أنت سيد قرارك هنا، فليس لديك أداة إدارة حزم موثوقة لتضمن أمان الحزمة. كن حذرًا جدًا وتأكد أن ما تضعه في السكربت لن يضر بأنظمة المستخدمين أو بياناتهم؛ خصيصًا عندما تفعل أشياءً تتطلب امتيازات الجذر (root – أي الامتيازات الإدارية)؛ لن يساعدك خسارة عدد كبير من الأشخاص لبياناتهم بسبب تطبيقك في حملتك التسويقية له. اكتب دليلا لاستعمال تطبيقك تمكن الآن المستخدمون من تثبيت مشروعك بسهولة، وعليك الآن أن تريهم طريقة استعماله. حان الوقت الآن لكتابة دليل المستخدم؛ وليس من الضرورة أن يكون هذا الدليل طويلًا ومفصلًا؛ فيجب أن يشرح الواجهة الأساسية، ويشير إلى أيّة حدود للتطبيق. لا تكتب كثيرًا من المعلومات التقنية وركِّز على ما يحتاج المستخدم الاعتيادي إلى معرفته. لا تجاهد لكي يكون الدليل مفصلًا وكاملًا من البداية؛ ابدأ من أهم النقاط ثم أكمل الباقي أثناء تطويرك للمشروعك، وستستفيد من أسئلة المستخدمين الجدد الذين يحاولون استعمال مشروعك في كتابة محتوى الدليل. أفضل بنية وأدوات لكتابة الدليل تعتمد على طبيعة وحجم مشروعك، وعمّا إذا كنت تولِّد الدليل من الشيفرة المصدرية أم كنت تكتبه بنفسك بأكمله يدويًا. يجب أن يَسهُل البحث في الدليل النهائي (حتى لو وضعت كل المعلومات في صفحة وحيدة واستعملت خاصية البحث في المتصفح). لا تنس الرخصة خطأٌ شائعٌ يقلل كثيرًا من استعمال مشروعك هو عدم وجود رخصة مفتوحة المصدر؛ تأكد أن شيفراتك تأتي مع رخصة معترف بها من FSF أو OSI؛ راجع الدرس كيف تختار رخصة مفتوحة المصدر لبرامجك لمزيدٍ من المعلومات حول هذا الصدد. ما التالي؟ أصبح مشروعك الآن جاهزًا للاستعمال من المستخدمين، فحان الوقت لنشره، وهذا هو موضوع المقالة القادمة. ترجمة -وبتصرّف- للمقال Make your open-source project accessible لصاحبه Radek Pazdera.
  10. هل سبق وأن نشرتَ مشروعًا مفتوح المصدر ولم تشاهد حشدًا كبيرًا من الناس آتين لتنزيله بالآلاف، مما يعطل الخواديم في أول ليلة بعد إصداره؟ حسنًا، لا تتشكل أغلبية المجتمعات (communities) بين ليلةٍ وضحاها؛ وأصبح من الصعب جذب المساهمين -بوجود العدد الكبير من المشاريع المتوفرة في أيامنا هذه- دون القيام ببعض التسويق. لكن أغلبنا -معشرَ المبرمجين- لسنا معتادين على التسويق أو مرتاحين بفعله؛ وبذلك ستبقى الشيفرات التي نكتبها تقبع مهجورةً بالمستودعات ولا يأبه أحدٌ بأمرها، فما الحل؟ هذه السلسلة من المقالات ستأخذنا برحلةٍ عبر عددٍ من الأمور التي عليك فعلها كي تساعد باكتشاف مشروعك ولتُسهِّل على الآخرين الاشتراك به. هذا الدرس هو القسم الأول من سلسلة التسويق للمشاريع مفتوحة المصدر وسيركِّز على كيفية التعريف بمشروعك. الجمهور المستهدف وصفحة الهبوط (هذا الدرس) كيف تجعل الوصول إلى مشروعك مفتوح المصدر أسهل طرق التعريف بمشروعك مفتوح المصدر كيف تحول مستخدمي مشروعك مفتوح المصدر إلى مساهمين كيف تنمي المجتمع الخاص بمشروعك مفتوح المصدر يبدأ المجتمع بالمستخدمين السؤال الذي يبدأ الأشخاص بسؤاله عادةً هو "كيف أتمكن من جعل الآخرين يساهمون في مشروعي؟" وهذا السؤال -في غالب الأحيان- سابقٌ لأوانه. فقبل التفكير بالمساهمين، عليك التفكير بالمستخدمين. فدون أن يكون لديك قاعدة مستخدمين كبيرة (نسبيًا)، سيكون من الصعب جذب أيّة مساهمين على الإطلاق. فهل تساهم أنت في مشاريع لم تستعملها قط؟ ربما لا، وكذلك سيفعل البقية. سأفترض أنك أنشأت مشروعك ليحل مشكلةً واجَهتك من قبل، وربما تواجه الكثيرين غيرك، كل ما عليك فعله هو أن تجدهم. اكتشف من هم جمهورك المستهدف تعتبر أصغر المشاريع "منتجاتٍ"، فهي تحل مشكلة أو تلبي حاجة ما؛ فما هي المشكلة التي يحلها مشروعك؟ ومَن الذي سيراه مفيدًا؟ سيساعدك التفكير بهذين السؤالين على توجيه جهودك والتركيز على من يهتمون دون أن "تزعج" من لا يأبهون بمشروعك بتاتًا. هل أنشأت إضافةً لمحرر vim (محرر سطري مشهور جدًا على الأنظمة الشبيهة بيونكس)؟ ربما أنت تبحث عن الأشخاص الذين يستعملون vim، وقد لا يكون وضع تلك الإضافة في "emacs subreddit" أفضل شيءٍ تفعله. ابحث عن أولائك الأشخاص وتواصل معهم وانضم إليهم، تنجذب مجموعات من ذوي الاهتمامات المتشابهة نحو أماكن معينة أو قنوات اتصال مخصصة، وربما لديهم اجتماعات محلية أو subredits خاصة بهم. بينما مثالنا عن vim السابق بسيط، لكن الأمر ليس بهذا الوضوح خصيصًا بجمهورٍ شغوفٍ ومتحمسٍ كالمبرمجين، فيمكن أن توقع نفسك بنقاشاتٍ محتدمة (يسميها معشر المبرمجين بالمصطلح "flame wars")، وفي بعض الأحيان لا يكتفون بتجاهلك وإنما سينالون من هيبة مشروعك؛ فاستهداف الأشخاص الصحيحين يعني أيضًا أنك لن تزعج أولاءك الذين لا يشاركونك رأيك. إعداد صفحة هبوط (Landing page) قبل إخبار الناس عن مشروعك، عليك أن تهيّء مكانًا لترسلهم إليه يحتوي كل ما يحتاجونه للبدء باستعمال مشروعك؛ وهذا المكان قد يكون ملف README على Github، أو تدوينة، أو موقع مخصص لذلك. يجب على تلك الصفحة أن تلخص ماذا يفعل مشروعك، ومن أين ستحصل عليه، وكيف تجعله يعمل، وقد تنتهي (اختياريًا) بمرجع مُبسَّط وسريع حول المهام أو المشاكل الشائعة؛ فعليك تبيان الحدود القصوى لمشروعك، لكن لا تعرضها في البداية، بل ركز على ما يبرع تطبيقك بفعله ودع تلك الحالات لتكتبها في التوثيق. إن كان يعطي تطبيقك مخرجاتٍ مرئيةً من أي نوع، أو كانت له واجهةٌ رسومية، فلا تنسَ أن تضيف لقطاتٍ للشاشة، وإن لم تكن مخرجاته مرئيةً، لا بأس من أخذ لقطات للطرفية (مكان إظهار نواتج الأوامر) أو عمل صور متحركة (gifs) لها؛ ضع بعين الاعتبار أنَّ الناس سيفهمون تطبيقك أسرع "بمشاهدته" أكثر من مجرد القراءة عنه. في النهاية، يجب أن توفر صفحة الهبوط طرقًا للتواصل معك أو للمجتمع المحيط بالمشروع ليحصلوا على الدعم؛ جهّز قائمةً بريديةً، أو قناة IRC، أو غرفة على Gitter ووفرها لهم؛ أما للمشاريع الصغيرة، فقد يكفي وضع بريدك الإلكتروني. عليك أن تراعي عادات الفئة المستهدفة، فلن يُسعَد ثلةٌ من خبراء أمن المعلومات بالتسكع في مجموعتك على فيس بوك. عليك بعد أن تجهز صفحتك أن تنشرها في مختلف أماكن تواجد المبرمجين مثل موقع Hacker News و r/programming (على reddit)؛ لكن جهودك تلك ليست موجهة لفئة معيّنة، وتعتمد على الحظ كثيرًا؛ سأتحدث عن طرقٍ أفضل لنشر مشرعك في مقالاتٍ قادمة من هذه السلسلة، ابقَ معنا! عندما تصل إلى مرحلة إنشاء الصفحة، تذكر أنَّه لا يهم شكلها بقدر أهمية محتواها؛ وفي الواقع، عليك أن تقضي وقتًا أطول بالتفكير بمحتواها أكثر من بقية الأمور. هل أنت مطور واجهات محترف يتمكن من تصميم موقع مصمم تصميمًا رائعًا في ساعتين؟ هذا جميل اذهب وصمم أجمل ما تستطيع، لكن لا تنفق ثلاثة أشهر وأنت تضيع وقتك على كم يجب أن تكون قيمة الهامش لعناوين الصفحة مثلا فالأمر لا يستحق كل ذلك العناء. ملف README على GitHub سيكون كافيًا عادةً. ما التالي؟ سنتحدث في المقالة القادمة عن جعل مشروعك أكثر قابليةً للوصول للمستخدمين. فالأسوأ من عدم القدرة على فهم ما الذي يفعله المشروع هو تضييع ثلاث ساعات لمحاولة جعله يعمل! ترجمة -وبتصرّف- للمقال Marketing for open-source projects لصاحبه Radek Pazdera.
  11. عندما نكتب برنامجًا طويلًا جدًا، فإن الشيفرة ستوزَّع في عددٍ من الملفات والأصناف (classes) والدوال (functions)؛ الدوال هي مجموعة من التعليمات التي تنجز مُهمِّة معيّنة وهدفها ألا تكرر الشيفرات التي تكتبها؛ فمثلًا في نظام للاستيثاق، يكون هنالك دوال لتسجيل الدخول (login) والتسجيل (register) وتسجيل الخروج (logout). أبسط أشكال الدوال إن الشكل العام لأبسط دالة هو: <?php function function_name() { // الشيفرة } ?> وكما في المتغيرات، تستطيع إسناد أي اسم إلى الدالة (أعطينا الدالة في المثال السابق الاسم function_name). تُكتَب الشيفرة التي ستنفذها الدالة عندما يتم استدعاؤها بين أقواس معقوفة ({})؛ نستطيع استدعاء الدالة عبر كتابة اسمها ويليه أقواس عادية () كما في المثال الآتي: <?php // يمكنك أن تستدعي الدالة هنا قبل تعريفها // تعريف دالة ذات الاسم say_hello function say_hello() { // كل ما تفعله هذه الدالة هو طباعة hello echo "hello"; } // استدعاء الدالة say_hello(); ?> الدوال ذات الوسائط يمكننا تمرير المعلومات الإضافية (مثل اسم المستخدم وكلمة المرور) إلى الدوال باستخدام الوسائط (arguments)، وعلينا تعريف المعاملات (parameters). ملاحظة: الوسائط هي البيانات التي نمررها إلى الدالة عندما نستدعيها، أما المعاملات فهي المتغيرات التي نُعرِّفها عندما نكتب الشيفرة الخاصة بالدالة. يمكننا أن نعتبر أن المعاملات هي حاويات والوسائط هي البيانات التي نضعها في تلك الحاويات. <?php // دالة تسجيل الدخول ذات وسيطين function login($user, $pass) { // تطبع هذه الدالة اسم المستخدم وكلمة المرور echo "hello, username is $user and password is $pass"; } // استدعاء الدالة login("user1","UsEr!p@$$W0rD"); // استدعاء الدالة مرةً أخرى login("user2","simplepassword"); ?> المعاملات ذوات القيم الافتراضية يمكننا أن نعطي قيمًا افتراضية للمعاملات لكي يمكن تجاوز تحديد قيمتها عند استدعاء الدالة. <?php // دالة تسجيل الدخول ذات ثلاثة وسائط function login($user, $pass, $active = "not active") { // تعرِض هذه الدالة اسم المستخدم وكلمة مروره وحالته echo "hello, username is $user and password is $pass and user is $active"; } // استدعاء الدالة login('user1','UsEr!p@$$W0rD'); // استدعاء الدالة مرةً أخرى login('user2','simplepassword', 'active'); ?> ملاحظة: إن أردت ألّا تُحدِّد قيمةً لوسيطٍ ما عند استدعاء الدالة، فيجب أن يكون المعامل الموافق لذاك الوسيط في آخر القائمة كما في المثال السابق. ويملك المعامل افتراضيًا القيمة "null". الدوال ذات المعاملات متغيرة العدد يمكن أن تملك الدوال عددًا متغيرًا من المعاملات؛ وفي الواقع، تستطيع تمرير إي عدد من الوسائط إلى دالة؛ استعمل الدوال المُضمَّنة في PHP الآتية للوصول إلى تلك الوسائط: الدالة func_get_args()‎: تُعيد مصفوفة بجميع الوسائط المُمرَّرة الدالة func_num_args()‎: تُعيد العدد الإجمالي للوسائط المُمرَّرة. الدالة func_get_arg(argument_number)‎: تُعيد المتغير ذو الرقم argument_number سنظهِر -في المثال الآتي- مجموع الأرقام المُمرَّرة إلى الدالة كوسائط بالإضافة إلى عرضها. <?php function sum() { // سنستعمل func_get_args() للحصول على كل المعاملات على شكل مصفوفة $ary = func_get_args(); print_r($ary); echo "<br>"; $sum = 0; // سنستعمل func_num_args للحصول على العدد الكلي للمعاملات for($i = 0; $i < func_num_args(); $i++) { // سنستعمل func_get_arg(index) للحصول على قيمة معامل معيّن echo func_get_arg($i).'<br>'; $sum+= func_get_arg($i); } echo "sum is $sum <br><br>"; } sum(1,2,3,4); sum(23,2343,54,2,1,6); ?> ناتج تنفيذ السكربت السابق هو: Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) 1 2 3 4 sum is 10 Array ( [0] => 23 [1] => 2343 [2] => 54 [3] => 2 [4] => 1 [5] => 6 ) 23 2343 54 2 1 6 sum is 2429 إعادة القيم من الدوال لاحظ أننا في المثال السابق طبعنا السلسلة النصية مباشرةً، لكن ماذا لو أردنا أن نجعل الدالة تُعيد قيمةً ما؟ نستعمل العبارة البرمجية return لإعادة قيمة من الدالة، وتُسبِّب هذه العبارة بإنهاء تنفيذ الدالة مباشرةً وإعادة القيمة إلى مكان استدعاء الدالة، لنرى مثالًا عن دالة تُعيد مربَّع العدد المُمرَّر إليها: <?php function square ($num) { return $num * $num; } // الناتج هو 16 echo square (4); ?> لاحظ أنك لا تستطيع إعادة أكثر من قيمة من الدالة، لكن يمكنك إعادة مصفوفة التي تؤدي نفس الدور تقريبًا. <?php function powers ($num) { return array ($num, $num * $num, $num * $num * $num); } // الناتج هو: Array ( [0] => 4 [1] => 16 [2] => 64 ) print_r(powers (4)); ?> مجالات تعريف الدوال تستطيع أن تستدعي الدوال المُعرَّفة في الأمثلة السابقة قبل مكان تعريفها، لأن مجالها (scope) هو المجال العام، يجدر بالذكر أنَّك تستطيع تعريف الدوال داخل جملة شرطية (if) لكن لا يمكنك استدعاؤها إن لم يتحقق الشرط كما في المثال الآتي: <?php $str = 'create the function'; global_function(); // لا يمكننا استدعاء الدالة not_global هاهنا، لأن المفسر لا يعتبرها موجودةً إن لم تتحقق الجملة الشرطية الآتية if ($str == 'create the function') { function not_global() { echo 'I don\'t exist until program execution reaches me'; } } // نستطيع الآن استدعاء الدالة not_global بعد اختبار أن السلسلة $str مساوية للقيمة اعلاه، لتجنب إظهار خطأ if ($str == 'create the function') not_global(); function global_function() { echo 'I exist immediately upon program start'; } ?> الأمر سيان لآلية تعريف دالة داخل دالة أخرى: <?php function global_function() { function not_global() { echo 'I don\'t exist until global_function() is called'; } } // لا يمكننا استدعاء not_global هنا، إذ علينا استدعاء global_function أولًا global_function(); // نستطيع الآن استدعاء الدالة not_global، لأن استدعاء global_function قد جعلها متاحةً للمفسر not_global(); ?> المتغيرات في الدوال مجال المتغيرات (variable scope) هو "المجال" الذي يبقى فيه المتغير مُعرَّفًا، أي بكلامٍ آخر، المتغيرات المُعرَّفة داخل الدوال لا يمكن الوصول إليها من خارج تلك الدوال؛ أما المتغيرات المُعرَّفة في المجال العام (global scope) يمكن الوصول إليها من أي جزء من البرنامج في المجال العام؛ ربما أربكك الشرح السابق لكن انظر إلى هذا المثال للتوضيح: <?php // المتغير $a في المجال العام $a = 1; function test() { echo $a; // لا يمكن الوصول إلى المتغير $a لأنه في المجال العام } // لن يظهر أي شيء ﻷن المتغير $a ليس ضمن مجال الدالة test(); // سيظهر الرقم 1، لأن المجال هو العام echo $a; ?> لن تظهر مخرجات عند استدعاء الدالة test()‎ لأن الدالة echo داخلها تُشير إلى نسخة محلية من المتغير ‎$a التي لم تُسنَد لها قيمة في ذاك المجال. يمكن الوصول إلى المتغيرات في المجال العام من داخل الدوال باستخدام الكلمة المحجوزة global كما يلي: <?php $a = 1; $b = 2; function sum (){ // أصبح بإمكاننا الوصول إلى المتغيرين في مجال الدالة الخاص، وتعديل قيمتهما مباشرةً global $a, $b; $b = $a + $b; } sum(); echo $b; ?> نوعٌ أخيرٌ هو المتغيرات الثابتة (static variables) التي يُسمَح بوجودها في مجال الدوال الخاص فقط، لكنها لا تفقد قيمتها عند انتهاء تنفيذ الدالة، انظر إلى المثال الآتي لتوضيح الأمور: <?php function test() { $a = 0; echo $a; $a++; } ?> الدالة السابقة عديمة الفائدة لأنها في كل مرة تُستدعى فيها ستضبط قيمة المتغير ‎$a إلى 0، ثم ستُظهِر قيمة ذاك المتغير (التي هي صفر)، وأخيرًا لن نستفيد من زيادة قيمة المتغير (‎$a++‎) لانتهاء تنفيذ الدالة ولن يعود المتغير ‎$a موجودًا. أما لو عرَّفنا المتغير ‎$a على أنه ثابت، فلن يفقد قيمته بعد انتهاء تنفيذ الدالة: <?php function test() { static $a = 0; echo $a; $a++; } ?> ستتم تهيئة المتغير ‎$a في أول مرة تُستدعى فيها الدالة، وستُطبَع قيمة المتغير وستزداد قيمته في كل مرة تستدعى فيها الدالة مرةً أخرى. <?php function test() { static $a = 0; echo $a; $a++; } //الناتج 0 test(); //الناتج 1 test(); //الناتج 2 test(); ?> ملاحظة: يجب إسناد قيم بسيطة إلى المتغيرات الثابتة عند تعريفها، إذ لا يجوز استعمال التعابير الرياضية أو إسناد القيم المُعادة من الدوال إليها. المعاملات المرجعية رأينا كيف أنَّ جميع الدوال السابقة تعيد قيمة ما بناءً على العمليات التي تُجرى فيها، لكنها لا تستطيع تعديل قيم الوسائط المُمرَّرة إليها، لكن "التمرير بالمرجعية" (pass by reference) يسمح للدوال بتعديل قيم الوسائط مباشرةً، لنأخذ مثالًا بسيطًا يوضِّح هذا الأمر: <?php function test(&$var) { $var++; // لاحظ عدم استعمال عبارة return هنا } $a = 5; test($a); echo $a; // الناتج هو 6 ?> الاختلاف الوحيد الذي يجعل الوسائط تُمرَّر بمرجعيتها هو وضع محرف «&» قبل اسم الوسيط عند تعريف الدالة. الدوال المجهولة الدوال المجهولة (anonymous functions) التي تُعرَف أيضًا بالمصطلح "closures" تسمح بإنشاء دالة ليس لها اسم محدد، وإنما تُسند مباشرةً إلى متغير مَثَلُها كَمَثَلِ أيّةِ عملية إسنادٍ أخرى، وتنتهي عملية الإسناد أيضًا بفاصلة منقوطة؛ هذا مثالٌ عنها: <?php $square = function ($num) { echo $num * $num; }; $square(2); // الناتج 4 $square(15); // الناتج 225 ?> نستطيع أن نُمرِّر إلى الدوال المجهولة أي متغير موجود في مجال المتغيرات الأعلى منها وذلك باستخدام الكلمة المحجوزة use، تأمّل في المثال الآتي وفي تعليقاته: <?php $message = 'hello'; // لم نستعمل الكلمة المفتاحية use هنا $example = function () { echo $message; }; $example(); // لن تَظهَر أيّة قيمة (أو بالتحديد، سيظهر خطأ Notice ﻷن المتغير غير مُعرَّف) // تمرير المتغير $message $example = function () use ($message) { echo $message; }; $example(); // الناتج: hello // القيمة المُمرّرة تؤخذ قبل تعريف الدالة، وليس قبل استدعائها $message = 'world'; $example(); // الناتج: hello // إعادة قيمة المتغير إلى قيمته الأصلية $message = 'hello'; // الوراثة بالمرجعية (inherit by-reference) $example = function () use (&$message) { echo $message; }; $example(); // الناتج: hello // إذا تغيرت قيمة المتغير الآن، فستتأثر الدالة المجهولة بها $message = 'world'; $example(); // الناتج: world // استعمال الوسائط العادية $example = function ($arg) use ($message) { echo $arg . ' ' . $message; }; $example("hello"); // الناتج: hello world ?> تمرين اكتب دالةً تقبل وسيطين أولهما هو طول المستطيل والآخر هو عرض المستطيل، وستعيد هذه الدالة أربع قيم تُمثِّل إحداثيات نقاط التقاء أضلاعه على فرض أنَّ مركزه يقع على مبدأ الإحداثيات. المصادر مقال Functions in php لصاحبه Harish Kumar. مقالتي Functions و Closures لصاحبهما Dayle Rees. صفحات Functions و Anonymous functions و User-defined functions في دليل php وغيرها.
  12. سنعلم في هذا الدرس عن المصفوفات وحلقة foreach. لنفترض أنَّك تريد أن تُخزِّن مقدارًا كبيرًا من البيانات؛ أول 10000 من مضاعفات الرقم 5 على سبيل المثال، فعليك أنَّ تُعرِّف 10000 متغير لهم 10000 اسم؛ وهذه مهمة صعبة ومضنية بالتأكيد. أحد الحلول هو تخزين البيانات في مصفوفة، إذ يمكننا تخزين أي عدد نريده من القيم في مصفوفة وحيدة فقط! لنلقِ نظرةً عن آلية عمل المصفوفات. <?php // تعريف مصفوفة ذات ثلاثة عناصر $type1 = array('first element', 'second element', 'third element'); // نستعمل الدالة print_r لطباعة جميع عناصر المصفوفة // الناتج: Array ( [0] => first element [1] => second element [2] => third element ) print_r($type1); echo "<br>"; // نستعمل المفاتيح (الفهارس) للوصول إلى قيم معيّنة في المصفوفة // لاحظ أنَّ المفاتيح رقمية من 0 حتى 2 echo $type1[0]; echo "<br>"; echo $type1[1]; echo "<br>"; echo $type1[2]; echo "<br>"; ?> في السطر الثالث من الشيفرة السابقة، عرَّفنا مصفوفةً اسمها ‎ $type1وملأناها بثلاث قيم (first element، و second element، و third element). كيف يمكننا استعمالها الآن؟ كل قيمة من القيم المُخزَّنة في المصفوفة مرتبطة بمُعرِّف فريد يسمى الفهرس (index)؛ يمكنك الوصول إلى تلك القيم عبر الفهرس الخاص بها. يطبع السطر السابع في المثال السابق المصفوفة بأكملها باستعمال الدالة print_r()‎، وسترى أنَّه على الرغم من أنَّك قد ملأت القيم فقط، لكنك ستحصل أيضًا على أرقامٍ تبدأ من الصفر وترتبط بتلك القيم؛ وفي الواقع، تلك الأرقام هي الرقم التسلسلي للقيمة (تذكر أننا نبدأ العد في البرمجة من الرقم 0 بدلًا من 1). ثم قمنا بطباعة القيم التي خزناها في الموضع 0 و 1 و 2 على التوالي وبالترتيب. المصفوفات الترابطية استخدمنا الفهارس الافتراضية في المثال السابق (0، و 1، و2)، لكن يمكننا أن نُعرِّف الفهارس يدويًا كما في المثال الآتي: <?php // طريقةٌ أخرى لتعريف مصفوفة $type2 = array('key'=>'value', 'key2'=>'value2', 'key3'=> 'value3'); // الناتج: Array ( [key] => value [key2] => value2 [key3] => value3 ) print_r($type2); echo "<br>"; // المفاتيح هنا نصيّة، وهي key1، و key2، و key3 echo $type2['key3']; ?> عرَّفنا في المثال السابق الفهارس الخاصة بنا (تُسمى أيضًا المفاتيح [keys]) التي ترتبط مع تلك القيم. يسمى هذا النوع من المصفوفات بالمصفوفات ترابطية (associative arrays). الوصول إلى عناصر المصفوفات تُعتبَر كل قيمة في المصفوفة متغيرًا مستقلًا، فيمكننا أن نجري جميع العمليات التي يمكن القيام بها على المتغيرات (كالزيادة والنقصان وإسناد القيم …إلخ.): <?php // نعرِّف مصفوفةً فارغةً $arr = array(); // يمكننا إسناد البيانات إلى عناصر المصفوفة كما يلي $arr[0] = 'academy'; $arr[1] = 'hsoub'; // قد لا تكون المفاتيح ذات أرقام متتالية (لاحظ عدم وجود المفتاحين 2 و 3) $arr[4] = 'com'; // يمكننا استعمال نمطي المفاتيح (الرقمية والنصية) في نفس المصفوفة $arr['day'] = 'Monday'; $arr['num1'] = 4; $arr['num2'] = 5; // كل عنصر في المصفوفة مستقل تمامًا عما سواه $arr['num3'] = $arr['num1'] + $arr['num2']; // سنطبع كامل المصفوفة باستعمال print_r print_r($arr); ?> لاحظ كيف أننا عرفنا بدايةً مصفوفةً فارغة، ثم أضفنا عناصرها بذكر المتغير الحاوي على مصفوفة متبوعًا بأقواسٍ مربعة ([]) بداخلها مفتاح العنصر. يجدر بالذكر أننا نستطيع أن نستعمل مفاتيح رقمية ونصية في نفس المصفوفة. إذا أردنا أن نجعل PHP تُرقِّم عناصر المصفوفة تلقائيًا عند إسناد قيم لها، فيكفي أن نضع أقواسًا مربعة فارغة كما يلي: <?php // نعرِّف مصفوفةً فارغةً $arr = array(); // يمكننا جعل PHP تُرقِّم العناصر تلقائيًا كما يلي $arr[] = 'academy'; $arr[] = 'hsoub'; $arr[] = 'com'; // سنطبع كامل المصفوفة باستعمال print_r // الناتج: Array ( [0] => academy [1] => hsoub [2] => com ) print_r($arr); ?> يجدر بالذكر أننا نستطيع أن ندرج قيمة عنصر من عناصر المصفوفة في سلسلةٍ نصيةٍ مباشرةً كما في المتغيرات (أي يجب أن تكون علامة الاقتباس مزدوجةً) لكن علينا في المصفوفات أن نحيط اسم العنصر بقوسين معقوفين كما يلي (لاحظ أننا استعملنا الطريقة الأكثر شيوعًا الآن في تعريف المصفوفات، ألا وهي إحاطة عناصر المصفوفة بقوسين مربعين [] كما يظهر في المثال أدناه): المصفوفات والحلقات لنفترض أننا نريد إجراء بعض العمليات على كمية كبيرة من البيانات مخزَّنة في مصفوفة (ولنقل أننا نريد طباعة البيانات، أو حساب مجموع الأرقام الموجودة في المصفوفة)؛ فسنستعمل لهذا الغرض الحلقات، لنلقِ نظرةً على مثالٍ عن حلقة for: <?php $a = [1, 21, 23, 43, 32, 23, 4]; // عناصر المصفوفة هي: $a[0], $a[1], $a[2]... // سنُهيِّئ متغيرًا للمجموع ونسند إليه القيمة 0 $sum = 0; for($i = 0; $i < 7; $i++) { // ستزداد قيمة المتغير $i في كل تكرار للحلقة // أي أن قيمته ستتغير إلى قيمة مفتاح العنصر التالي في المصفوفة $sum += $a[$i]; } echo $sum; ?> أنصحك بأن تُعيد حلّ المثال السابق باستعمال حلقتَيّ while و do-while من درس الحلقات لكي تتدرب على استعمالها. هنالك حلقةٌ إضافيةٌ في PHP وُضِعَت خصيصًا للمصفوفات، اسمها حلقة foreach. إذ تبدأ هذه الحلقة من أول عنصر في المصفوفة وتنتهي بآخر عنصر فيها. لنعد كتابة المثال السابق باستخدام حلقة foreach: <?php $a = [1, 21, 23, 43, 32, 23, 4]; // عناصر المصفوفة هي: $a[0], $a[1], $a[2]... // سنُهيِّئ متغيرًا للمجموع ونسند إليه القيمة 0 $sum = 0; foreach ($a as $v) { // لو كنا نستعمل حلقة من نوع آخر، لكنا قد كتبنا // $v = $a[$i]; // $sum += $v; // i++; $sum += $v; } echo $sum; ?> نُسنِد -في كل تكرار للحلقة- قيمةً جديدةً للمتغير الذي اسمه ‎$v ونعطيه قيمة العنصر التالي في المصفوفة ‎$a؛ حلقة foreach مفيدة جدًا عندما لا تعرف عدد العناصر الموجودة في المصفوفة، أو إذا كنت تستعمل مصفوفة ترابطية (associative arrays). حلقة foreach مع المصفوفات الترابطية أما في حالة المصفوفات الترابطية، فعلينا تعريف متغيرين (بدلًا من واحد) لأننا لا نعلم قيمة الفهرس (أو المفتاح [key]) لكل عنصر. أول متغير نُعرِّفه هو المفتاح، والثاني هو القيمة التي ترتبط به. الشكل العام لحلقة foreach هو: foreach (array as $key => $value) { statements } تمعّن في المثال الآتي وانظر كيف أُسنِد المفتاح إلى متغير، وأُسنِدَت قيمته إلى متغيرٍ آخر؛ يجدر بالذكر أنَّ المصفوفة المستعملة هي مصفوفة متعددة الأبعاد، أي أنَّ قيمة العنصر هي بدورها مصفوفةٌ أخرى، ولا مانع من استعمال حلقة foreach داخل حلقة foreach أخرى إن دعت الضرورة إلى ذلك. <?php // نُعرِّف مصفوفة ذات بعدين $arr = [ 'student1' => array('SN' => 16472, 'math_grade' => 'A+'), 'student2' => array('SN' => 16483, 'math_grade' => 'C'), 'student3' => array('SN' => 16587, 'math_grade' => 'A') ]; // حلقة foreach تمر على جميع عناصر المصفوفة الرئيسية foreach ($arr as $student => $info) { // لاحظ كيف أنَّ القيمة المُنسدَة إلى المتغير $info هي مصفوفة أخرى echo "the serial number for $student is {$info['SN']}, and he got {$info['math_grade']} grade in math!"; } ?> دوال التعامل مع المصفوفات ألم تتساءل كيف سنعلم إن كانت المصفوفة تحتوي عنصرًا ما أم لا؟ ربما فكرت باستعمال حلقةٍ ما للمرور على جميع عناصر المصفوفة، لكن PHP تسهِّل عليك الأمر وتوفر لك دالةً لاختبار وجود عنصرٍ ما في المصفوفة ألا وهي الدالةin_array() ‎ التي تقبل وسيطين أولهما هو القيمة التي سيُبحَث عنها، وثانيهما هي المصفوفة التي سيُبحَث فيها؛ وستعيد هذه الدالة القيمة TRUE إن وجِدَت تلك القيمة في المصفوفة، و FALSE خلا ذلك. أما لو أردت معرفة إن كان مفتاحٌ ما موجودًا في المصفوفة، فاستعمل الدالةarray_key_exists() ‎ التي تقبل وسيطين أيضًا أولهما هو المفتاح الذي يُبحَث عنه، وثانيهما هو المصفوفة التي سيُبحَث فيها؛ وستعيد هذه الدالة TRUE إن وجد ذاك المفتاح، و FALSE عدا ذلك. مثالٌ عن استعمالهما: <?php $arr = ['subdomain' => 'academy', 'domain' => 'hsoub', 'root_domain' => 'com']; // التحقق إن كانت القيمة «hsoub» موجودةً في المصفوفة if (in_array('hsoub', $arr)) { echo '"hsoub" value found'; } if (array_key_exists('subdomain', $arr)) { echo '"subdomain" key found'; } ?> هل تتذكر كيف قسّمنا السلاسل النصية إلى قسمين يفصل بينهما محرف معيّن في درس السلاسل النصية وحصلنا على الجزء الواقع بعد ذاك المحرف؟ الدالة explode()‎ تفعل المِثل، لكنها تُخزِّن الناتج في مصفوفة، حيث تقبل وسيطين أولهما هو السلسلة النصية التي تُمثِّل الفاصل بين عناصر المصفوفة، وثانيهما هو السلسلة النصية التي تريد تقسيمها؛ انظر إلى ناتج المثال الآتي لإزالة الغموض عن الكلام السابق: <?php $str = 'hsoub academy is the best academy ever'; // سنقسم السلسلة النصية السابقة عند كلمة is، ثم سنطبع مخرجاتها $arr1 = explode('is', $str); // الناتج: Array ( [0] => hsoub academy [1] => the best academy ever ) print_r($arr1); // سنقسم السلسلة النصية السابقة عند كلمة academy، مما يُنتِج ثلاثة عناصر في المصفوفة $arr2 = explode('academy', $str); // الناتج: Array ( [0] => hsoub [1] => is the best [2] => ever ) print_r($arr2); ?> أما لو أردت دمج عناصر المصفوفة وتحويلها إلى سلسلة نصية، فاستعمل الدالة implode()‎؛ التي لها شكلان عامان أحدهما يقبل وسيطًا وحيدًا هو المصفوفة التي تريد تحويلها إلى سلسلة نصية؛ والشكل الثاني يقبل وسيطين أولهما هو السلسلة النصية التي ستوضع بين عناصر المصفوفة وثانيهما هو المصفوفة التي تريد تحويلها إلى سلسلة نصية. <?php $arr = ['academy', 'hsoub', 'com']; // إن استعملنا implode ذات الشكل الأول، فسنحصل على academyhsoubcom $str1 = implode($arr); echo $str1; // أما لو استعملنا الشكل الثاني، ومررنا «.» كوسيط، فسنحصل على academy.hsoub.com $str2 = implode('.', $arr); echo $str2; ?> يُلخِّص الجدول الآتي بعض دوال المصفوفات الشهيرة:. table{border: 1px solid, black; border-collapse: collapse;} th, td{border: 1px solid black;padding: 5px 10px;} th{background-color: #fbfcfc;} الدالة شرحها array_merge()‎ يُمرَّر إليها أكثر من مصفوفة كوسيط مهمتها هي دمج تلك المصفوفات مع بعضها بعضًا وإعادة المصفوفة الناتجة array_rand()‎‎ اختيار قيمة عشوائية واحدة أو أكثر تقبل وسيطًا إجباريًا هو المصفوفة التي ستؤخذ القيمة العشوائية منها ووسيطًا اختياريًا يُحدِّد عدد القيم التي ستُعاد تُعيد سلسلةً نصيةً تحوي مفتاح العنصر العشوائي المُختار إن اختارت قيمةً عشوائيةً وحيدة تعيد مصفوفة بمفاتيح العناصر العشوائية إن كان الوسيط الثاني أكبر من 1 ()array_search تبحث في المصفوفة عن قيمة مُعيّنة وستُعيد المفتاح المقابل لها إن وُجِدَت تقبل وسيطين أولهما هو القيمة التي ستبحث عنها، وثانيهما هو المصفوفة التي سيُبحَث فيها array_sum()‎ حساب مجموع القيم العددية في مصفوفة تقبل وسيطًا وحيدًا هو المصفوفة المُراد حساب مجموع الأعداد فيها ()array_unique تُزيل هذه الدالة جميع التكرارات في مصفوفة تُمرَّر إليها كوسيط ()count ربما هذه من أهم الدوال السابقة، إذ تُعيد عدد العناصر الموجودة في مصفوفة تُمرَّر إليها كوسيط. نستفيد كثيرًا من هذه الدالة في حلقة for إن لم نكن نعرف عدد عناصر المصفوفة نستطيع أن نكتب: for (int i=0; i<array.count(); i++) {} تمرين لديك المصفوفة الآتية باسم names: Array ( [16472] => Ayham [16483] => Ameen [15789] => Bashir ) والمصفوفة باسم grades: Array ( [16472] => Array ( [math] => 62 [arabic] => 76 [english] => 75 ) [16483] => Array ( [math] => 85 [arabic] => 71 [english] => 82 ) [15789] => Array ( [math] => 52 [arabic] => 86 [english] => 93 ) ) اكتب برنامجًا يأخذ اسم الطالب ثم يطبع جميع علاماته. المصادر مقال Array and foreach loop in PHP لصاحبه Harish Kumar. كتاب تعلم البرمجة بلغة PHP. صفحات Arrays و array وما هو موجود في Array Functions.
  13. ربما تستخدم grep فاستعماله أسهل وأيسر من nano أو vi (كلاهما محرران نصيان، ولا حاجة لفتح محرر للبحث عن كلمة). grep search_word file_name لمزيدٍ من المعلومات حول grep (وغيره من الأدوات السطرية) راجع كتاب «سطر أوامر لينُكس»
  14. حسب الغاية والغرض، إذ تشبه رخصةُ «وقف» رخصةَ «GPLv2» (انظر هذا الموضوع للاستزادة)؛ أما المشاع الإبداعي، فهو مجموعة رخص وليست رخصةً وحيدةً (انظر إنفوجرافيك «تعرف على المشاع الإبداعي» لمزيدٍ من المعلومات).
  15. ما الذي يجعل مشروعك مفتوح المصدر؟ أن تكون الشّيفرة متوفرةً مجانًا على الإنترنت؟ أو أن تستطيع استعماله، أو تعديله وإرساله إلى صديقك؟ إذا ابتغينا الدقة، فإن الرخصة هي التي تعطيك الامتيازات لفعل كل ما سبق ذكره. فعندما "تفتح" مصدر مشروعك، فعليك أن تُضمِّن ملف رخصة يُحدِّد ما هي الشروط التي سيُسمَح للآخرين باستعمال مشروعك وفقًا لها. لحسن الحظ، هنالك خياراتٌ عديدةٌ يمكنك الاختيار بينها، فلا حاجة إلى أن تكون محاميًا لفعل ذلك؛ لكن لسوء الحظ هنالك الكثير من الرخص، مما يجعلك تحتار أيهم ستختار. تنبيه: هذه هي طريقة ترخيص مشاريعي الخاصة، لكنني لست محاميًا وهذه المقالة لا تُمثِّل نصيحة قانونية. ما هي الرخص؟ عدد الرخص التي يمكن اعتبارها "حرة" (free) أو مفتوحة المصدر (open-source) بالمئات! إذا كنت تريد قوائم طويلة، فراجع القوائم الموجودة على موقع مشروع GNU و opensoucre.org أو على ويكيبيديا، وحتى تلك القوائم الطويلة ليست شاملة لجميع الرخص. وعلى الرغم من التعداد الكبير للرخص، لكن الفروقات بينها ليست محورية؛ والسبب وراء وجود عدد كبير منها هو أنَّ كاتبيها صعيبو المراس في اختيار الكلمات وبعض التفاصيل، لكن يمكن اعتبار شروط الكثير منها متماثلة. ادخل إلى موقع tl;drLegal لمراجعة سريعة لشروط مختلف الرخص. الرخص المتساهلة و Copyleft أكبر أمر يُفرِّق بين الرخص هو Copyleft، وهو مصطلحٌ أوجده مشروع غنو (GNU) لمنع الأشخاص الذين سيعيدون توزيع البرمجيات في المستقبل من تقييد الحريات التي أعطيتَها للمشروع عند إطلاقه، وهذا يعني أنَّه على أيّ شخصٍ يريد أن يُعيد توزيع نسخةٍ مُعدَّلةٍ من الشيفرة التي كتبتَها أن ينشر تعديلاته أيضًا. تُطبِّق بعض الرخص ذاك المبدأ (مثل GPL، و LGPL، وMPL) بينما لا تُطبقه الأخرى (مثل MIT، و Apache، و BSD). قد تكون رخص "copyleft" مفيدةً جدًا لمنع إساءة استعمال مشروعك، وقد تكون في بعض الأحيان معيقةً لاستعماله من الشركات التي قد لا تقدر على استعمال شيفرات مرخصة بتلك الرخصة في برمجياتها التجارية. مجال مشروعك أحد أهم الأشياء التي يجب أخذها بعين الاعتبار عند اختيار رخصة لمشروعك هو "المجال"؛ هل هو مكتبة برمجية، أم أداة للمطورين، أم تطبيق كامل للمستخدم النهائي؟ إذا كان سيُستعمَل مع مكتباتٍ أخرى، فعليك أن تكون حذرًا في اختيارك للرخصة بسبب مشاكل في التوافقية بين الرخص (سنشرح ذلك بعد قليل). أختارُ للتطبيقات الكاملة أو المنتجات، مثل تطبيقات الأندرويد أو تطبيقات سطح المكتب أو الأدوات التي تعمل من سطر الأوامر، رخصًا من نمط copyleft مثل GPLv3 التي تطمئنني أنَّ المشروع سيبقى مفتوح المصدر دومًا. وعندما تُطلِقُ مكتبة أو إطار عمل ليستعمله المطورون في مشاريعهم، فإن اختيارك سيصبح أكثر صعوبةً. فعدم السماح لهم بتوزيع برمجياتهم التي تعتمد على مكتبتك دون التضمين الكود المصدري قد يمنع الشركات من استعمالها في مشاريعهم، مما يمنع انتشارها انتشارًا واسعًا. شخصيًا، أستعمل رخصًا متساهلة في هذه الحالة، مثل MIT أو BSD؛ وبينما تترك تلك الرخص احتمال أن تشتق الشركات مصدر المشروع الخاص بك، وتطوره ولا تعطيك التعديلات عليه، لكن ذلك غير عملي أو منطقي لكثيرٍ من الشركات؛ لأن الاختلاف من مصدر الشيفرة الأصلي سيجعلهم يتحملون عبء تكاليف الصيانة التي تتجاوز عادةً قيمة التّعديل الذي أجروه على شيفرة مشروعك. رخصة LGPL ليست خيارًا سيئًا أيضًا، إذ تسمح للآخرين باستعمال نسخة مُصرَّفة (compiled) من المكتبة مع شيفراتهم المملوكة (proprietary أو الاحتكارية)، وفي نفس الوقت ستحافظ على حقوق مصدر المكتبة. المشاكل في التوافقية تحتوي بعض الرخص بنودًا تتعارض مع غيرها من الرخص؛ مما يجعلها غير متوافقة، مما يعني أنك لا تستطيع أن تدمج بين حزمتين برمجيتين أو مكتبتين مرخصتين برخصتين فيهما بنود متعارضة. انظر إلى الحزم البرمجية التي تستعملها في مشروعك، وحاول أن تختار رخصةً لا تتعارض مع بنود تلك الحزم. هنالك مصادرٌ عدِّة تستطيع الحصول على معلومات توافقية الرخص منها، بما في ذلك ويكيبيديا. تؤثر عادةً المشاكل في التوافقية على الرخص المعقَّدة والمحدَّدة مثل GPLv3؛ فكلما ازداد طول الرخصة وتخصيصها للبنود، كما ازدادت احتمالية حدوث مشاكل في التوافقية. تحقق من مجتمعك اعتمادًا على التقنيات التي يستعملها مشروعك، قد تجد أنَّ إحدى الرخص أفضل وأنسب من الأخرى، آخذًا بعين الاعتبار سهولة دمج مشروعك وتبنيه، وخصيصًا لو كان مكتبةً. فاستعمال أكثر رخصة شائعة في مجالك ستُسهِّل الأمر على مستعمليها، لأنهم سيكونون معتادين على شروط تلك الرخصة، وسيتم تقليل احتمالية وجود تعارض في الرخص في المشاريع. على سبيل المثال، مجتمعَا JavaScript و Ruby يُحبذون الرخص الأكثر سماحيةً مثل MIT، بينما تُنشَر المشاريع المكتوبة بلغة C/C++‎ برخصة GPL. عليك أن تبحث قليلًا في المجتمع التطويري المحيط بك عندما تنشر مكتبة برمجية وفق رخصةٍ معينة، فذاك المجتمع قد يساعدك بقرارك. لكن لا تُكرِه نفسك على رخصة معينة لأن الآخرين يستعملونها، فقد لا تكون خيارًا صائبًا لمشروعك. الخلاصة هي أنه اختيارك سيكون سديدًا إن كانت تتوافق الرخصة التي اخترها مع أغلبية المكتبات في مجتمعك. بدون رخصة إحصائيات الاستخدام التي نُشِرَت من Ben Balter على مدونة Github في مطلع عام 2015 تُظهِر أنَّ حوالي 80% من المستودعات على الموقع لا تُضمِّن رخصة؛ وهذا يعني أنَّ لا يُسمَح لأي شخص قانونيًا أن يستعمل الشيفرة الخاصة بهم حتى لو كانت متوفرة على الإنترنت لأنه لا يمكن اعتبارها "مفتوحة المصدر". هذا أمرٌ كارثي! إن لم يكن هذا ما تطمح له، فخذ وقتك للتفكير برخصة مناسبة، وإلا فلن "يلمس" أي مبرمج خبير الشيفرة الخاصة بك، ويعرض سمعته للخطر بدعوى قضائية. الخلاصة هذه هي الطريقة التي أتبعها لاختيار رخصة لمشروعي. لا يوجد خيار صائب أو خيار خاطئ في اختيارك للرخصة إن كنت تعي ما تفعل. اختر واحدةً تناسب احتياجاتك، وتأكد أن تختار رخصةً واحدةً على الأقل. ما هي الرخصة التي استعملتها لآخر مشروعٍ لك؟ أخبرنا في التعليقات. ترجمة -وبتصرّف- للمقال How to pick an open source licence for your code لصاحبه Radek Pazdera.
  16. هل تحاول متابعة آخر التحديثات لعدِّة مشاريع على GitHub؟ إذًا أنت لست حديث العهد باستقبال العديد من الإشعارات حول: التبليغ عن العلل، ونشر تعليقات، وقبول طلبات إضافة (pull requests). تتطلب بعض الأمور السابقة تدخلًا منك، وعليك أن تعلم عن بعضها الآخر، والباقي مجرد ضوضاء. هذه مُرشِّحات (filters) لبريد Gmail التي أستعملها لكي أحصل على الإشعارات المفيدة بسرعة. لدى GitHub مركز إشعارات خاص به يسمح بترشيح الإشعارات لكل مشروع، بالإضافة إلى إظهار الإشعارات للأشياء التي تُشارِك فيها فقط. وكل إشعار له أيقونة خاصة به تُحدِّد ما إن كان مشكلةً أو طلبَ دمجٍ وسواءً بقي مفتوحًا/أو لم يُدمَج بعد. هذا مفيدٌ حقًا، ويقلل الوقت اللازم لتفقد اللائحة كل يوم؛ على سبيل المثال، ربما تريد تخطي المشكلات التي تم إغلاقها أو الطلبات التي تم دمجها، وتتطلع بسرعة على المشكلات التي لم تُذكَر فيها، وتُركِّز على ما أنت مشاركٌ فيه. ماذا لو كنت تفضل التعامل مع الإشعارات عبر البريد الإلكتروني؟ أنا أفضل أن يكون كل شيءٍ موجودًا في مكانٍ واحد في صندوق الوارد. لكني سأفوت على نفسي كل الميزات الموفرة للوقت التي يعطيني إياها مركز الإشعارات. لن يكون المرور على الإشعارات واحدًا واحدًا ذا إنتاجيةٍ عالية. لكن لحسن الحظ، يمكن جلب بعض ميزات مركز الإشعارات إلى بريدك الإلكتروني باستعمال "المرشحات" (filters)، وهذه هي المرشحات التي أستعملها مع Gmail. وسم جميع الإشعارات من GitHub لأنني أستقبل عددًا كبيرًا من الرسائل من GitHub، فأحب أن أوسمها (tag) جميعًا كي أميز بينها وبين الرسائل الأخرى بسهولة؛ ومن النادر أن تكون إشعارات GitHub ذات أولويةً عالية، ووسمها كلها سيسمح لي بإخفائها بسهولة كي أتأكد أنني لم أفوِّت رسالةً مهمةً. لضبط ما سبق، رشِّح حقل "From:‎" للبريد "notifications@github.com"، يبدو هذا في Gmail كالآتي: from:(notifications@github.com) التكليفات أحاول أن أهتم بما أنا مكلفٌ به، ولهذا أعلِّم تلك الرسائل بوسم "Assignment"، وعندما أكون مشغولًا، أنظر إلى هذه القائمة فقط لكي أتأكد أنَّ كل شيءٍ أنا مسؤولٌ عن إنجازه قد أُنجِز. عندما يُسنِد أحدهم مشكلةٍ إليك، فسيُرسِل GitHub تنبيهًا، ويمكن تعليم (أو توسيم) كامل الموضوع (thread) كتكليفٍ إليك عبر مطابقة هذه الرسالة التنبيهية. سيبدو مُرشِّح Gmail كالآتي (ابحث عن معرفك@ Assigned to): from:(notifications@github.com) Assigned to @pazdera هنالك بعض المحدوديات لهذه الطريقة لسوء الحظ، فلن يُرسَل إليك إشعارٌ إن أسندتَ المشكلة إلى نفسك ولن يُوسَّم الموضوع بشكلٍ صحيح في هذه الحالة؛ وإن أعيد إسناد المشكلة إلى شخصٍ آخر، فسيبقى الموضوع موسمًا على أنك مكلّف بالمشكلة. ذكر معرفك (Mentions) ربما هذا المرشح هو أكثرهم فائدةً لأنه يسمح لك برؤية الرسائل التي ذُكِرتَ فيها دون الحاجة إلى النظر إلى جميع الرسائل غير المهمة بحثًا عن مُعرِّفك على GitHub، فستعلم تمامًا أين طُلِبَت مداخلتك. استعمل مُرشِّحًا يطابق مُعرِّفك على GitHub في جسد الرسالة كما يلي: from:(notifications@github.com) @pazdera طلب pull request تم دمجه Merged لن تكون -في أغلب الأوقات- هنالك حاجةٌ لمداخلتك عندما يُدمج طلب Pull Request ويمكنك تجاوزها في أغلب الأحيان (خصيصًا إن لم تُذكَر في النقاش في طلب Pull Request ‏[PR]). يُرسِل GitHub إشعارًا يمكِّنك من إنشاء مُرشِّح لتوسيم الموضوع (thread) على أنه Merged. from:(notifications@github.com) Merged للأسف، لا يوجد دعم للتعابير النمطية (regular expressions) في Gmail، ولهذا سيُطابِق المرشح السابق التعليقات التي يذكر فيها صاحبها الكلمة "Merged". لكن -وإن كان يبدو ذلك مشكلةً كبيرةً- من النادر حدوث ذلك حسب تجربتي. مشكلة تم إغلاقها Closed في نهاية المطاف، من المفيد -عندما تُغلق مشكلة- أن تعرف عن ذلك دون الحاجة إلى النقر على الرسالة لفتحها. أستعمل المُرشِّح الآتي لتعليم كل تلك الرسائل للحذف مباشرةً. from:(notifications@github.com) Closed \# هذه هي المرشحات الخمسة التي تساعدني في التخلص من الكم الكبير من تنبيهات GitHub في بريدي. ما الذي تستعمله كيلا تقضي ساعاتٍ في بريدك؟ شارك ذلك في التعليقات أدناه. ترجمة -وبتصرّف- للمقال ‎5 Useful Gmail Filters for GitHub Users لصاحبه Radek Pazdera.
  17. هنالك العديد من البرمجيات المفيدة جدًا المطورة من فريق خادوم أوبنتو وغيرهم التي تندمج اندماجًا جيدًا مع نسخة خادوم أوبنتو، لكن ربما لا تكون معروفةً جدًا؛ سيعرض هذا الدرس بعض التطبيقات المفيدة التي تسهِّل إدارة خادوم، أو عدِّة خواديم أوبنتو. pam_motd عندما تسجل دخولك إلى خادوم أوبنتو، ربما تلاحظ "رسالة اليوم" (Message Of The Day اختصارًا MOTD)؛ تأتي هذه المعلومات وتُعرَض من حزمتين: 1. الحزمة landscape-common: توفر المكتبات الأساسية لبرمجية landscape-client، التي يمكن أن تُستخدَم لإدارة الأنظمة باستخدام تطبيق الويب Landscape؛ تتضمن هذه الحزمة الأداة ‎/usr/bin/landscape-sysinfo التي تُستخدَم لجمع المعلومات التي تُعرَض في MOTD، مثل المعالج، والذاكرة، والمساحة التخزينية للقرص الصلب ...إلخ. على سبيل المثال: System load: 0.0 Processes: 76 Usage of /: 30.2% of 3.11GB Users logged in: 1 Memory usage: 20% IP address for eth0: 10.153.107.115 Swap usage: 0% Graph this data and manage this system at https://landscape.canonical.com/ ملاحظة: يمكنك تشغيل الأمر landscape-sysinfo في أي وقت يدويًا. 2. حزمة update-notifier-common: التي توفر معلومات عن التحديثات المتوفرة للحزم، والتحققات من أنظمة الملفات (fsck)، ومتى يجب إعادة الإقلاع (مثلًا، بعد تحديث النواة). تنفِّذ pam_motd السكربتات في ‎/etc/update-motd.d في ترتيبٍ مبنيّ على الرقم الذي يسبق اسم السكربت؛ يُكتَب ناتج السكربتات إلى ‎/‎var/run/motd، بترتيبٍ رقمي، ثم تُجمَّع مع ‎/etc/motd.tail. يمكنك إضافة البيانات الديناميكية إلى رسالة اليوم؛ فمثلًا، لإضافة معلومات الطقس المحلي: أولًا، ثبِّت حزمة weather-util: sudo apt-get install weather-util تستخدم أداة الطقس بيانات METAR من National Oceanic and Atmospheric Administration and Forecast من National Weather Service؛ وللعثور على المعلومات المحلية، فستحتاج إلى رمز ICAO من أربعة محارف؛ الذي يمكن تحديده بتصفح موقع http://www.weather.gov/tg/siteloc.shtml. وعلى الرغم من أن National Weather Service هي وكالة حكومية تابعة للولايات المتحدة، لكن هنالك محطات طقس متوفرة في جميع أنحاء العالم، لكن ربما لا تتوفر معلومات الطقس لجميع المناطق خارج الولايات المتحدة. أنشِئ الملف ‎/usr/local/bin/local-weather، الذي هو سكربت شِل بسيط للحصول على الطقس لمنطقتك المحلية: #!/bin/sh # # # Prints the local weather information for the MOTD. # # # Replace KINT with your local weather station. # Local stations can be found here: http://www.weather.gov/tg/siteloc.shtml echo weather -i KINT echo اجعل السكربت قابلًا للتنفيذ: sudo chmod 755 /usr/local/bin/local-weather ثم أنشِئ وصلةً رمزيةً إلى ‎/etc/update-motd.d/98-local-weather: sudo ln -s /usr/local/bin/local-weather /etc/update-motd.d/98-local-weather في النهاية، أغلق جلستك الحالية، وأعد تشغيل الدخول لمشاهدة رسالة اليوم الجديدة. يجب أن يُرحَّب بك الآن ببعض المعلومات المفيدة؛ لكن بعض المعلومات حول الطقس المحلي قد لا تكون مفيدةً جدًا! لكن هذا المثال يشرح مرونة pam_motd. etckeeper يسمح etckeeper بتخزين محتويات ‎/etc/‎ بسهولة في مستودع نظام تحكم بالإصدارات (VCS)؛ حيث يندمج مع apt لكي يودع التغيرات الحاصلة على ‎/etc تلقائيًّا عندما تُثبَّت أو تُحدَّث الحزم. وضع ‎/etc ضمن مستودع للتحكم بالإصدارات هو أفضل ممارسة يُنصَح بها في مجال العمل، وهدف etckeeper هو جعل هذه المهمة أسهل ما يمكن. أدخِل الأمر الآتي في الطرفية لتثبيت etckeeper: sudo apt-get install etckeeper ملف الضبط الافتراضي ‎/etc/etckeeper/etckeeper.conf هو بسيط جدًا؛ الخيار الرئيسي يكون لضبط أي متحكم بالإصدارات ليُستخدَم؛ افتراضيًا، يكون etckeeper مضبوط لاستخدام Bazaar للتحكم بالإصدارات؛ ويُهيَّأ المستودع تلقائيًّا (ويُودَع فيه لأول مرة) أثناء عملية تثبيت الحزمة؛ من الممكن التراجع عن هذه الخطوة بإدخال الأمر: sudo etckeeper uninit سيودع etckeeper التغيرات غير المودعة التي حصلت على ‎/etc يوميًا افتراضيًا؛ يمكن تعطيل هذا باستخدام خيار الضبط AVOID_DAILY_AUTOCOMMITS؛ وستودع أيضًا التغيرات تلقائيًا قبل وبعد تثبيت الحزم؛ للمزيد من القدرة على التحكم بالتغيرات، من المستحسن أن تودع التغيرات يدويًا مع رسالة الإيداع كما يلي: sudo etckeeper commit "..Reason for configuration change.." يمكنك باستخدام أوامر VCS مشاهدة سجل المعلومات حول الملفات في ‎/etc: sudo bzr log /etc/passwd لشرح طريقة الاندماج مع نظام إدارة الحزم، جرِّب تثبيت الحزمة postfix: sudo apt-get install postfix بعد انتهاء التثبيت، ستودَع كل ملفات ضبط postfix إلى المستودع: Committing to: /etc/ added aliases.db modified group modified group- modified gshadow modified gshadow- modified passwd modified passwd- added postfix added resolvconf added rsyslog.d modified shadow modified shadow- added init.d/postfix added network/if-down.d/postfix added network/if-up.d/postfix added postfix/dynamicmaps.cf added postfix/main.cf added postfix/master.cf added postfix/post-install added postfix/postfix-files added postfix/postfix-script added postfix/sasl added ppp/ip-down.d added ppp/ip-down.d/postfix added ppp/ip-up.d/postfix added rc0.d/K20postfix added rc1.d/K20postfix added rc2.d/S20postfix added rc3.d/S20postfix added rc4.d/S20postfix added rc5.d/S20postfix added rc6.d/K20postfix added resolvconf/update-libc.d added resolvconf/update-libc.d/postfix added rsyslog.d/postfix.conf added ufw/applications.d/postfix Committed revision 2. وكمثال عن طريقة تتبع etckeeper للتغيرات اليدوية، أضف مضيفًا جديدًا إلى ملف ‎/etc/hosts؛ ثم استخدام bzr لمشاهدة أي ملفات قد عُدِّلَت: sudo bzr status /etc/ modified: hosts يمكنك إيداع التغيرات الآن: sudo etckeeper commit "new host" للمزيد من المعلومات حول bzr، راجع درس نظرة سريعة على Bazaar، نظام التحكم في الإصدارات على أوبنتو. Byobu أحد أكثر البرامج فائدةً لأي مدير أنظمة هو screen، حيث يسمح بتنفيذ عدِّة صدفات (shells) في طرفية واحدة؛ ولجعل بعض ميزات screen المتقدمة أكثر قربًا من المستخدم، ولتوفير بعض المعلومات المفيدة عن النظام؛ أنشِئت الحزمة byobu. عند تنفيذ byobu، سيُظهِر الضغط على زر F9 قائمةَ الضبط التي تسمح لك بما يلي: عرض قائمة المساعدة. تغيير لون خلفية Byobu. تغيير لون أمامية Byobu. تبديل ظهور شريط الإشعارات. تغيير ربط المفاتيح. تغيير سلسلة الخروج. إنشاء نوافذ جديدة. إدارة النوافذ الافتراضية. «لا يبدأ Byobu عند تسجيل الدخول (تفعيل ذاك الخيار)". ربط المفاتيح يحدد بعض الأمور مثل سلسلة الخروج (escape sequence)، وإنشاء نافذة جديدة، وتغيير النافذة ...إلخ. هنالك مجموعتا ربط للمفاتيح يمكن الاختيار بينها، واحدة باسم f-keys، والأخرى screen-escape-keys؛ إذا أردت استخدام الربط الافتراضي، فاختر none. يوفر byobu قائمةً تُظهِر إصدارة أوبنتو، ومعلومات المعالج، ومعلومات الذاكرة، والوقت والتاريخ؛ مما يجعلها تبدو كقائمة سطح مكتب. تفعيل خيار "لا يبدأ Byobu عند تسجيل الدخول" سيجعل byobu يبدأ عند فتح أي طرفية؛ التغيرات التي تحصل على byobu تكون خاصة بالمستخدم، ولن تؤثر على بقية مستخدمي النظام. أحد الميزات في byobu هو نمط scrollback، اضغط على زر F7 للدخول بوضع scrollback، الذي يسمح لك بالتنقل إلى المخرجات السابقة باستخدام أوامر شبيهة بأوامر محرر vi؛ هذه قائمة سريعة بأوامر الحركة: h: تحريك المؤشر إلى اليسار محرفًا واحدًا. j: تحريك المؤشر إلى الأسفل سطرًا واحدًا. k: تحريك المؤشر إلى الأعلى سطرًا واحدًا. l: تحريك المؤشر إلى اليمين محرفًا واحدًا. : تحريك المؤشر إلى بداية السطر الحالي. $: تحريك المؤشر إلى نهاية السطر الحالي. G: تحريك المؤشر إلى سطر محدد (افتراضيًا إلى النهاية). ?: البحث إلى الخلف. n: الانتقال إلى المطابقة التالية إما إلى الأمام أو إلى الخلف. مصادر راجع صفحة الدليل man update-motd للمزيد من الخيارات المتوفرة لحزمة update-motd. راجع موقع etckeeper لمزيدٍ من التفاصيل حول استخدامه. راجع أيضًا صفحة ويكي أوبنتو "etckeeper". لآخر الأخبار عن bzr، انظر إلى موقع bzr الرسمي. لمزيد من المعلومات حول screen، راجع موقعه الرسمي. وأيضًا صفحة ويكي أوبنتو "Screen". راجع صفحة مشروع Byobu لمزيدٍ من المعلومات. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: Other Useful Applications.
  18. تطرّقنا في الدّرس السّابق إلى ضبط بسيط جدًا لـ VPN، يمكن للعميل الوصول إلى الخدمات على خادوم VPN عبر نفق مشفَّر؛ إذا أردت الوصول إلى المزيد من الخواديم أو أي شيء آخر على الشبكات الأخرى، فأعطِ العملاء بعض تعليمات التوجيه؛ على سبيل المثال، لو كان بالإمكان تلخيص شبكة شركتك بالنطاق 192.168.0.0/16؛ فيمكنك إعطاء هذا التوجيه إلى العملاء، لكن عليك أيضًا تغيير التوجيه لطريقة العودة، أي أن خادومك عليه أن يعرف طريقة العودة إلى شبكة عميل VPN. أو ربما تريد أن تعطي البوابة الافتراضية إلى جميع عملائك وترسل جميع البيانات الشبكية إلى بوابة VPN أولًا، ومن هناك إلى الجدار الناري للشركة ثم إلى الإنترنت؛ يوضح لك هذا القسم بعض الخيارات المتاحة أمامك. ضبط VPN موجه على الخادوم سيسمح إعطاء التوجيهات للعميل له بالوصول إلى شبكات فرعية أخرى خلف الخادوم؛ تذكر أن هذه الشبكات الفرعية يجب أن تعرف أن عليها إعادة توجيه الرزم التابعة لنطاق عناوين عميل OpenVPN ‏(10.8.0.0/24) إلى خادوم OpenVPN. push "route 10.0.0.0 255.0.0.0" ستضبط التعليمة السابقة جميع العملاء كي يعيدوا توجيه بوابة الشبكة الافتراضية عبر VPN، مما يؤدي إلى مرور جميع بيانات الشبكة كتصفح الويب أو طلبات DNS عبر VPN (خادوم OpenVPN أو الجدار الناري المركزي عندك الذي يحتاج إلى تمرير بطاقة TUN/TAP إلى الإنترنت لكي يعمل ذلك عملًا صحيحًا). اضبط نمط الخادوم ووفر شبكة VPN فرعية لكي يسحب OpenVPN عناوين العملاء منها؛ سيأخذ الخادوم العنوان 10.8.0.1 لنفسه، والبقية ستتوفر للعملاء؛ وكل عميل سيقدر على الوصول إلى الخادوم عبر 10.8.0.1. ضع تعليقًا قبل هذا السطر إذا كنت تستخدم جسر إيثرنت (ethernet bridging): server 10.8.0.0 255.255.255.0 حافظ على سجل لارتباطات عناوين IP للعملاء في هذا الملف؛ إذا توقف OpenVPN عن العمل أو أعيد تشغيله، فإن العملاء الذي سيعيدون إنشاء الاتصال سيُسنَد لهم نفس عنوان IP المُسنَد لهم سابقًا. ifconfig-pool-persist ipp.txt أضف خواديم DNS إلى العميل: push "dhcp-option DNS 10.0.0.2" push "dhcp-option DNS 10.1.0.2" اسمح بالتواصل من العميل إلى العميل: client-to-client تفعيل الضغط على خط VPN: comp-lzo تؤدي التعليمة keepalive بإرسال شبيهة برسائل ping مرارًا وتكرارًا عبر الخط الذي يصل بين الجانبين، لذلك سيعلم كل جانب متى ينقطع الاتصال عن الجانب الآخر؛ السطر الآتي سيرسل ping كل 1 ثانية، بافتراض أن الند البعيد سيكون متوقفًا إذا لم يَرِد رد على الرسالة خلال مدة 3 ثواني: keepalive 1 3 فكرةٌ جيدةٌ هي تقليص امتيازات عفريت OpenVPN بعد التهيئة: user nobody group nogroup يتضمن OpenVPN 2.0 خاصية تسمح لخادوم OpenVPN بالحصول الآمن على اسم مستخدم وكلمة مرور من العميل المتصل، ويستخدم هذه المعلومات كأساس للاستيثاق بالعميل؛ لاستخدام طريقة الاستيثاق هذه، أولًا أضف تعليمة auth-user-pass إلى ضبط العميل؛ التي ستوجه عميل OpenVPN لطلب اسم مستخدم وكلمة مرور، وتمريرها إلى الخادوم عبر قناة TLS آمنة. # client config! auth-user-pass هذا سيخبر خادوم OpenVPN أن يتحقق من اسم المستخدم وكلمة المرور المُدخَلة من العملاء باستخدام واحدة PAM لتسجيل الدخول؛ وهذا يفيد في حالة كان عندك آلية مركزية للاستيثاق مثل Kerberos. plugin /usr/lib/openvpn/openvpn-auth-pam.so login ضبط متقدم لخدمة VPN جسرية على الخادوم يمكن إعداد OpenVPN لكي يعمل بنمط VPN جسري (bridged VPN) أو موجَّه (routed VPN)؛ أحيانًا يُشار لذلك بخدمة VPN تعمل بالطبقة الثانية أو الثالثة من OSI؛ في VPN جسري، جميع الإطارات (frames) الشبكية تكون من الطبقة الثانية (layer-2)، أي جميع إطارات إيثرنت تُرسَل إلى شركاء VPN‏ (VPN partners)؛ بينما تُرسَل الرزم الشبكية من الطبقة الثالثة فقط إلى شركاء VPN‏ (VPN Partners)؛ في النمط الجسري، ستُرسَل جميع البيانات الشبكية بما التي تكون شبيهة بشبكة LAN مثل طلبات DHCP، و طلبات ARP ...إلخ إلى شركاء VPN، لكن في النمط الموجه، سيتم تجاهل تلك الرزم. تحضير ضبط بطاقة شبكية لإنشاء جسر على الخادوم تأكد من أن لديك الحزمة bridge-utils: sudo apt-get install bridge-utils قبل أن تضبط OpenVPN في النمط الجسري، عليك تغيير ضبط بطاقات الشبكة؛ لنفترض أن لدى خادومك بطاقة اسمها eth0 موصولة إلى الإنترنت، وبطاقة باسم eth1 موصولة إلى شبكة LAN التي تريد إنشاء جسر لها؛ سيبدو ملف ‎/etc/network/interfaces كما يلي: auto eth0 iface eth0 inet static address 1.2.3.4 netmask 255.255.255.248 default 1.2.3.1 auto eth1 iface eth1 inet static address 10.0.0.4 netmask 255.255.255.0 هذا ضبط بسيط للبطاقة ويجب أن يُعدَّل لكي يغيَّر إلى النمط الجسري حيث تتحول البطاقة eth1 إلى بطاقة br0 الجديدة؛ بالإضافة إلى أننا ضبطنا br0 لتكون البطاقة الجسرية للبطاقة eth1؛ علينا التأكد أن البطاقة eth1 دومًا في نمط تمرير الحزم: auto eth0 iface eth0 inet static address 1.2.3.4 netmask 255.255.255.248 default 1.2.3.1 auto eth1 iface eth1 inet manual up ip link set $IFACE up promisc on auto br0 iface br0 inet static address 10.0.0.4 netmask 255.255.255.0 bridge_ports eth1 يجب أن تشغِّل الآن تلك البطاقة؛ تحضَّر لأن هذا قد لا يعمل كما هو متوقع، وستفقد التحكم عن بعد؛ تأكد أنك تستطيع حل المشاكل بالوصول إلى الجهاز محليًا. sudo ifdown eth1 && sudo ifup -a إعداد ضبط الخادوم للجسر عدِّل الملف ‎/etc/openvpn/server.conf، مغيّرًا ما يلي من الخيارات إلى: ;dev tun dev tap up "/etc/openvpn/up.sh br0 eth1" ;server 10.8.0.0 255.255.255.0 server-bridge 10.0.0.4 255.255.255.0 10.0.0.128 10.0.0.254 ثم أنشِئ سكربتًا مساعدًا لإضافة البطاقة tap إلى الجسر، وللتأكد من أن eth1 في وضع تمرير الحزم؛ أنشِئ الملف ‎/etc/openvpn/up.sh: #!/bin/sh BR=$1 ETHDEV=$2 TAPDEV=$3 /sbin/ip link set "$TAPDEV" up /sbin/ip link set "$ETHDEV" promisc on /sbin/brctl addif $BR $TAPDEV ثم اجعل السكربت تنفيذًا: sudo chmod 755 /etc/openvpn/up.sh بعد ضبط الخادوم، عليك إعادة تشغيل خدمة openvpn بإدخال الأمر: sudo service openvpn restart ضبط العميل أولًا، ثبِّت openvpn على العميل: sudo apt-get install openvpn ثم بعد أن يكون الخادوم مضبوطًا، وشهادات العميل منسوخةً إلى ‎/etc/openvpn؛ فأنشِئ ملف ضبط للعميل بنسخ المثال، وذلك بإدخال الأمر الآتي في طرفية جهاز العميل: sudo cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf \ /etc/openvpn عدِّل الملف ‎/etc/openvpn/client.conf مغيّرًا الخيارات الآتية: dev tap ;dev tun ca ca.crt cert client1.crt key client1.key في النهاية، أعد تشغيل openvpn: sudo service openvpn restart يجب الآن أن تستطيع الوصول إلى شبكة LAN البعيدة عبر VPN. نسخ عميل OpenVPN الواجهة الرسومية لإدارة الشبكة في لينكس تأتي أغلبية توزيعات لينُكس بما فيها توزيعة أوبنتو للأجهزة المكتبية على برمجية «مدير الشبكة»، الذي هو واجهة رسومية جميلة لإدارة خيارات الشبكة؛ يمكنك أيضًا إدارة اتصالات VPN منها؛ تأكد أن لديك الحزمة network-manager-openvpn مثبتةً، ستلاحظ هنا أن تثبيتها سيثبِّت حزمًا أخرى مطلوبة: sudo apt-get install network-manager-openvpn لإعلام برمجية «مدير الشبكة» بتثبيت الحزم الجديدة، عليك إعادة تشغيله: restart network-manager network-manager start/running, process 3078 في واجهة مدير الشبكة، اختر لسان VPN واضغط على زر "إضافة"، ثم اختر OpenVPN كنوع خدمة VPN ثم اضغط على «إنشاء»، في النافذة التالية أضف اسم خادوم OpenVPN «كبوابة»، واختر «النوع» إلى «شهادات (TLS)» ثم وجِّه «شهادة المستخدم» إلى شهادتك، و «شهادة CA» إلى سلطة الشهادات التي تعتمدها، و «المفتاح الخاص» إلى ملف مفتاحك الخاص، استخدم الزر «خيارات متقدمة» لتفعيل الضغط أو غيره من الخيارات الخاصة التي ضبطتها على الخادوم؛ جرِّب الآن إنشاء اتصال عبر VPN. برمجية Tunnelblick للاتصال بخدمة OpenVPN مع واجهة رسومية لأنظمة ماك OS X إن Tunnelblick هو نسخة ممتازة حرة مفتوحة المصدر لواجهة رسومية لعميل OpenVPN لنظام ماك؛ نزِّل آخر نسخة من المثبِّت من الموقع الرسمي وثبتِّها؛ ثم ضع ملف الضبط client.ovpn مع الشهادات والمفاتيح سويةً في ‎ ‎/Users/username/Library/Application Support/Tunnelblick/Configurations/‎ ثم شغِّل Tunnelblick من مجلد «التطبيقات» لديك. # sample client.ovpn for Tunnelblick client remote blue.example.com port 1194 proto udp dev tun dev-type tun ns-cert-type server reneg-sec 86400 auth-user-pass auth-nocache auth-retry interact comp-lzo yes verb 3 ca ca.crt cert client.crt key client.key واجهة رسومية لعميل OpenVPN لويندوز نزِّل وثبِّت آخر نسخة من عميل OpenVPN لويندوز؛ يمكنك تثبيت واجهة رسومية اختيارية باسم OpenVPN Windows GUI؛ ثم عليك تشغيل خدمة OpenVPN، وذلك بالذهاب إلى «ابدأ - جهاز الكومبيوتر - إدارة - الخدمات» و «التطبيقات - الخدمات»، ثم اعثر على خدمة OpenVPN وشغِّلها، ثم اضبط نمط التشغيل إلى «تلقائي»؛ وعندما تشغِّل OpenVPN MI GUI لأول مرة، فعليك تشغيله كمدير؛ وذلك بالنقر عليه بالزر الأيمن وانتقاء الخيار المناسب. سيتوجب عليك كتابة ملف ضبط OpenVPN إلى ملف نصي ووضعه في C:\Program Files\OpenVPN\config\client.ovpn مع شهادة CA؛ وعليك وضع شهادة المستخدم في مجلد المنزل للمستخدم كما في المثال الآتي: # C:\Program Files\OpenVPN\config\client.ovpn client remote server.example.com port 1194 proto udp dev tun dev-type tun ns-cert-type server reneg-sec 86400 auth-user-pass auth-retry interact comp-lzo yes verb 3 ca ca.crt cert "C:\\Users\\username\\My Documents\\openvpn\\client.crt" key "C:\\Users\\username\\My Documents\\openvpn\\client.key" management 127.0.0.1 1194 management-hold management-query-passwords auth-retry interact ; Set the name of the Windows TAP network interface device here dev-node MyTAP وإذا لم ترد الاستيثاق من المستخدم أو كنت تريد تشغيل الخدمة دون تفاعله، فأضف تعليقًا قبل الخيارات الآتية: auth-user-pass auth-retry interact management 127.0.0.1 1194 management-hold management-query-passwords استخدام OpenVPN مع OpenWRT يوصف OpenWRT أنه توزيعة لينُكس للأجهزة المدمجة مثل موجهات WLAN؛ هنالك بعض الأنواع من تلك الموجهات التي أُعدَّت لتشغيل OpenWRT؛ بالاعتماد على الذاكرة المتوفرة في الموجه لديك، ربما تتمكن من تشغيل برمجيات مثل OpenVPN ويمكنك بناء موجه لمكتب فرعي مع إمكانية الاتصال عبر VPN إلى المكتب الرئيسي. سجِّل دخولك إلى OpenWRT وثبِّت OpenVPN: opkg update opkg install openvpn تفقَّد الملف ‎/etc/config/openvpn وضع ضبط العميل هناك؛ وانسخ الشهادة والمفاتيح إلى ‎/etc/openvpn: config openvpn client1 option enable 1 option client 1 # option dev tap option dev tun option proto udp option ca /etc/openvpn/ca.crt option cert /etc/openvpn/client.crt option key /etc/openvpn/client.key option comp_lzo 1 أعد تشغيل OpenVPN: service openvpn restart عليك أن ترى إذا كان عليك تعديل إعدادات الجدار الناري والتوجيه في موجهك. مصادر راجع موقع OpenVPN لمزيد من المعلومات. راجع كتاب «OpenVPN hardening security guide». أيضًا، الكتاب المنشور من Pakt باسم «OpenVPN: Building And Integration Virtual Private Networks» هو مرجع جيد. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: OpenVPN.
  19. إن OpenVPN هو حلّ لإنشاء شبكات وهمية خاصة (Virtual Private Networks اختصارًا VPN) موجودٌ في مستودعات أوبنتو؛ هو خدمة مرنة وعملية وآمنة، وينتمي إلى عائلة SSL/TLS VPN (التي تختلف عن IPSec VPN)؛ يشرح هذا الدرس تثبيت وضبط OpenVPN لإنشاء شبكة وهمية خاصة. OpenVPN إذا كنت تريد أكثر من مجرد مفاتيح مُشارَكة مسبقًا؛ فيجعل OpenVPN من السهل إعداد واستخدام بينة تحتية للمفتاح العمومي (Public Key Infrastructure اختصارًا PKI) لاستخدام شهادات SSL/TLS للاستيثاق ومبادلة المفاتيح بين خادوم VPN والعملاء؛ يمكن أن يُستخدَم OpenVPN في نمط موَجِّه أو جسر VPN‏ (routed or bridged VPN) ويمكن أن يُضبَط ليستخدم TCP أو UDP؛ ويمكن ضبط رقم المنفذ أيضًا، لكن رقم المنفذ 1194 هو الرقم الرسمي لهذه الخدمة؛ عملاء VPN موجودون تقريبًا في جميع توزيعات لينُكس، ونظام ماك OS X؛ وويندوز والموجهات (routers) التي تعتمد على OpenWRT. تثبيت الخادوم لتثبيت برمجية OpenVPN، أدخِل الأمر الآتي في الطرفية: sudo apt-get install openvpn إعداد البنية التحتية للمفتاح العمومي أول خطوة لضبط OpenVPN هي إنشاء بنية تحتية للمفتاح العمومي (PKI)؛ التي تحتوي على: شهادة منفصلة (تُسمى أيضًا مفتاح عمومي) وشهادة خاصة للخادوم ولكل عميل. شهادة سلطة شهادات (CA) رئيسية التي يمكن أن تُستخدَم لتوقيع شهادات كلٍّ من الخادوم والعملاء. يدعم OpenVPN الاستيثاق ثنائي الاتجاه بناءً على الشهادات، وهذا يعني أن على العميل الاستيثاق من شهادة الخادوم، وعلى الخادوم الاستيثاق من شهادة العميل قبل أن تُنشَأ ثقةٌ مشتركةٌ بينهما. على الخادوم والعميل الاستيثاق من بعضها أولًا عبر التحقق من أن الشهادة موقعة من سلطة الشهادات الرئيسية، ثم باختبار المعلومات في ترويسة الشهادة المستوثق منها؛ مثل اسم الشهادة الشائع أو نوع الشهادة (عميل أو خادوم). إعداد سلطة الشهادات لضبط سلطة شهادات خاصة بك وتوليد شهادات ومفاتيح لخادوم OpenVPN ولبعض العملاء، عليك أولًا نسخ المجلد easy-rsa إلى ‎/etc/openvpn؛ وهذا سيؤكد أن أي تغييرات إلى السكربتات لن تضيع عند تحديث الحزمة؛ أدخِل ما يلي في الطرفية: mkdir /etc/openvpn/easy-rsa/ cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/ الآن عدِّل الملف ‎/etc/openvpn/easy-rsa/vars مغيّرًا ما يلي ليناسب بيئتك: export KEY_COUNTRY="US" export KEY_PROVINCE="NC" export KEY_CITY="Winston-Salem" export KEY_ORG="Example Company" export KEY_EMAIL="steve@example.com" export KEY_CN=MyVPN export KEY_NAME=MyVPN export KEY_OU=MyVPN أدخِل ما يلي لتوليد شهادة سلطة شهادات رئيسية ومفتاح: cd /etc/openvpn/easy-rsa/ source vars ./clean-all ./build-ca شهادات الخادوم عليك توليد شهادة ومفتاح خاص للخادوم: ./build-key-server myservername وكما في الخطوة السابقة، أغلبية المعاملات يمكن أن تبقى على قيمتها الافتراضية؛ هنالك سؤالان يجب أن تجيب عليهما بالقبول هما: "‎Sign the certificate? [y/n]" و "‎‏‎1 out of 1 certificate requests ‎certified, commit? [y/n]‎". يجب توليد معاملات Diffie Hellman لخادوم OpenVPN: ./build-dh جميع الشهادات والمفاتيح ستولد في المجلد الفرعيkeys‎‎‎/؛ ومن العادات الشائعة بين المدراء نسخها إلى ‎/etc/openvpn: cd keys/ cp myservername.crt myservername.key ca.crt dh2048.pem /etc/openvpn/ شهادات العميل سيحتاج عميل VPN إلى شهادة أيضًا لكي يُعرِّف نفسه إلى الخادوم؛ عليك عادةً إنشاء شهادة منفصلة لكل عميل؛ أدخِل ما يلي في الطرفية لإنشاء شهادة: cd /etc/openvpn/easy-rsa/ source vars ./build-key client1 انسخ الملفات الآتية إلى العميل باستخدام طريقة آمنة: /etc/openvpn/ca.crt /etc/openvpn/easy-rsa/keys/client1.crt /etc/openvpn/easy-rsa/keys/client1.key ولأن شهادات ومفاتيح العميل مطلوبة فقط على حاسوب العميل، فعليك حذفهم من الخادوم. ضبط بسيط للخادوم ستحصل عند تثبيت OpenVPN على أمثلة عن ملفات الضبط: ls -l /usr/share/doc/openvpn/examples/sample-config-files/ total 68 -rw-r--r-- 1 root root 3427 2011-07-04 15:09 client.conf -rw-r--r-- 1 root root 4141 2011-07-04 15:09 server.conf.gz ابدأ بنسخ وفك ضغط server.conf.gz إلى ‎/etc/openvpn/server.conf. sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz \ /etc/openvpn/ sudo gzip -d /etc/openvpn/server.conf.gz عدِّل ملف ‎/etc/openvpn/server.conf للتأكد من أن الأسطر الآتية تشير إلى الشهادات والمفاتيح التي أنشأتها في القسم السابق: ca ca.crt cert myservername.crt key myservername.key dh dh2048.pem عدِّل الملف ‎/etc/sysctl.conf وأزل التعليق عن السطر الآتي لتفعيل تمرير IP: #net.ipv4.ip_forward=1 ثم أعد تحميل sysctl: sudo sysctl -p /etc/sysctl.conf هذا هو الحد الأدنى الذي تحتاج لضبط خادوم OpenVPN؛ يمكنك استخدام جميع الإعدادات الافتراضية في ملف server.conf؛ الآن شغِّل الخادوم، وستجد رسائل التسجيل والخطأ موجودةً في ملف syslog: sudo service openvpn start * Starting virtual private network daemon(s)... * Autostarting VPN 'server' [ OK ] تأكد الآن من أن OpenVPN قد أنشَأ البطاقة tun0: sudo ifconfig tun0 tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 inet addr:10.8.0.1 P-t-P:10.8.0.2 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1 [...] ضبط بسيط للعميل هنالك عدِّة نسخ من عملاء OpenVPN بواجهة أو بدون واجهة رسومية؛ يمكنك القراءة المزيد عن العملاء في قسمٍ آخر؛ لكننا الآن سنستخدم عميل OpenVPN في أوبنتو الذي هو نفس الملف التنفيذي للخادوم؛ لذلك عليك تثبيت الحزمة openvpn مرةً أخرى في جهاز العميل: sudo apt-get install openvpn سننسخ هذه المرة ملف مثال الضبط client.conf إلى ‎/etc/openvpn/‎: sudo cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf \ /etc/openvpn/ انسخ مفاتيح العميل والشهادة الصادرين من سلطة الشهادات التي أنشَأتها في قسمٍ سابق، وعدِّل ‎/etc/openvpn/client.conf للتأكد من أن الأسطر الآتية تُشير إلى تلك الملفات؛ يمكنك حذف المسار إذا كانت تلك الملفات موجودةً في ‎/etc/openvpn: ca ca.crt cert client1.crt key client1.key وعليك تحديد اسم أو عنوان خادوم OpenVPN واحد على الأقل؛ تأكد أن الكلمة المحجوزة client موجودةٌ في ملف الضبط، لأن هذا ما سيُفعِّل نمط العميل: client remote vpnserver.example.com 1194 شغِّل الآن عميل OpenVPN: sudo service openvpn start * Starting virtual private network daemon(s)... * Autostarting VPN 'client' [ OK ] وتأكد من إنشاء البطاقة الشبكية tun0: sudo ifconfig tun0 tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 inet addr:10.8.0.6 P-t-P:10.8.0.5 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1 وتأكد إن كان بإمكانك عمل ping لخادوم OpenVPN: ping 10.8.0.1 PING 10.8.0.1 (10.8.0.1) 56(84) bytes of data. 64 bytes from 10.8.0.1: icmp_req=1 ttl=64 time=0.920 ms ملاحظة: يستخدم خادوم OpenVPN أول عنوان IP قابل للاستخدام في شبكة العميل ويكون هذا العنوان هو الوحيد المستجيب للأداة ping؛ على سبيل المثال، لو ضُبِط قناع ‎/24 لشبكة العميل، فسيستخدم العنوان ‎.1؛ عنوان PTP (الند للند، أو peer to peer) الذي تراه في ناتج ifconfig أعلاه لا يجيب عادةً على طلبات ping. تأكد من جداول التوجيه عندك: sudo netstat -rn Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 10.8.0.5 0.0.0.0 255.255.255.255 UH 0 0 0 tun0 10.8.0.1 10.8.0.5 255.255.255.255 UGH 0 0 0 tun0 192.168.42.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 0.0.0.0 192.168.42.1 0.0.0.0 UG 0 0 0 eth0 أول خطوة في استكشاف الأخطاء إذا لم يعمل ما سبق لك، فعليك أن تفعل ما يلي: تحقق من سجل syslog عندك، أي grep -i vpn /var/log/syslog. هل يستطيع العميل الاتصال إلى الخادوم؟ ربما يحجب الجدار الناري وصوله؟ تأكد من سجل syslog على الخادوم. يجب أن يستخدم الخادوم والعميل نفس البروتوكول والمنفذ، مثلًا UDP بمنفذ 1194؛ راجع خيارَيّ الضبط proto و port. يجب أن يستخدم الخادوم والعميل نفس إعدادات الضبط الخاصة بالضغط، راجع خيار الضبط comp-lzo. يجب أن يستخدم الخادوم والعميل نفس الضبط المتعلق بنمط التوجيه والجسور. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: OpenVPN.
  20. ينشئ DRBD (الذي هو اختصار لـ Distributed Replicated Block Device والذي يُمكن ترجمته إلى «جهاز كتلي موزع ومُستنسَخ») نسخة انعكاسية من الأجهزة الكتلية بين عدِّة مضيفين؛ الاستنساخ غير مرئي لبقية التطبيقات على الأنظمة المضيفة. أي أقراص صلبة، أو أقسام، أو أجهزة RAID، أو حجوم منطقية ...إلخ. يمكن أن تُنسَخ انعكاسيًا (mirrored). للبدء باستخدام DRBD، عليك أولًا تثبيت الحزم الضرورية؛ وذلك بإدخال الأمر الآتي من الطرفية: sudo apt-get install drbd8-utils ملاحظة: إذا كنت تستخدم نواةً وهمية (virtual kernel) كجزءٍ من الآلة الوهمية، فستحتاج إلى تصريف (compile) وحدة debd؛ ربما من الأسهل تثبيت حزمة linux-server داخل الآلة الوهمية. يشرح هذا القسم كيفية ضبط debd لاستنساخ القسم ‎/srv بنظام ملفات ext3 بين مضيفَين؛ لا يهم حجم القسم، لكن يجب أن يكون كلا القسمَين بنفس الحجم. الضبط اسم المضيفين في هذا المثال هو debd01 و drbd02؛ وسنحتاج إلى الحصول على خدمة استبيان أسماء إما عبر DNS أو ملف ‎/etc/hosts؛ راجع درس DNS للتفاصيل. لضبط drbd، عدِّل ملف ‎/etc/drbd.conf على المضيف الأول: global { usage-count no; } common { syncer { rate 100M; } } resource r0 { protocol C; startup { wfc-timeout 15; degr-wfc-timeout 60; } net { cram-hmac-alg sha1; shared-secret "secret"; } on drbd01 { device /dev/drbd0; disk /dev/sdb1; address 192.168.0.1:7788; meta-disk internal; } on drbd02 { device /dev/drbd0; disk /dev/sdb1; address 192.168.0.2:7788; meta-disk internal; } } ملاحظة: هنالك خيارات أخرى كثيرة في ‎/etc/drbd.conf، لكن القيم الافتراضية كافيةٌ لهذا المثال. انسخ الآن الملف ‎/etc/drbd.conf إلى المضيف الثاني: scp /etc/drbd.conf drbd02:~ الآن، انسخ الملف إلى ‎ /‎etcفي drbd02: sudo mv drbd.conf /etc/ باستخدام أداة drbdadm لتهيئة تخزين البيانات الوصفية؛ نفِّذ على كل خادوم ما يلي: sudo drbdadm create-md r0 وعلى كلا المضيفين، شغِّل عفريت drbd: sudo service drbd start في drbd01، أو أي مضيف تريد أن يكون هو المضيف الرئيسي، أدخِل ما يلي: sudo drbdadm -- --overwrite-data-of-peer primary all ستبدأ البيانات بالمزامنة مع المضيف الثاني بعد تنفيذ الأمر السابق؛ نفِّذ الأمر الآتي على drbd02 لمشاهدة العملية: watch -n1 cat /proc/drbd اضغط Ctrl+c لإيقاف الأمر السابق. في النهاية، أضف نظام ملفات إلى ‎/dev/drbd0 وصِله: sudo mkfs.ext3 /dev/drbd0 sudo mount /dev/drbd0 /srv الاختبار لتختبر إذا كانت الملفات تُزامَن فعليًا بين المضيفين، فانسخ بعض الملفات في drbd01، إلى ‎/srv: sudo cp -r /etc/default /srv ثم افصل ‎/srv: sudo umount /srv الآن نزِّل مرتبة الخادوم الرئيسي إلى دور ثانوي: sudo drbdadm secondary r0 ورقّ الخادوم الثانوي إلى رئيسي: sudo drbdadm primary r0 ثم صِل القسم: sudo mount /dev/drbd0 /srv وباستخدام ls، يجب أن تشاهد ‎/srv/default منسوخةً من الخادوم الرئيسي (سابقًا) الذي هو drbd01. مصادر للمزيد من المعلومات حول DRBD، راجع الصفحة الرئيسية الخاصة به. تحتوي صفحة دليل man drbd.conf على شرح لخيارات لم نغطها في هذا الفصل. راجع أيضًا صفحة الدليل man drbdadm. صفحة ويكي أوبنتو «DRBD» فيها المزيد من المعلومات. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: DRBD.
  21. مجموعات التحكم هي آلية في النواة لتجميع وتتبع ووضع حد لاستهلاك الموارد للمهام؛ الواجهة الإدارية التي توفرها النواة تكون عبر نظام ملفات وهمي؛ لكن طوِّرت أدوات إدارية لمجموعات التحكم ذات مستوى أعلى، بما فيها libcgroup و lmctfy. بالإضافة لذلك، هنالك دليل في freedesktop.org حول كيف يمكن أن تتعاون التطبيقات بأفضل طريقة باستخدام واجهة نظام الملفات لمجموعات التحكم (cgroup filesystem interface). في أوبنتو 14.04؛ أصبح مدير مجموعات التحكم (cgmanager) متوفرًا كأداة أخرى لإدارة واجهة cgroup؛ حيث هدفه هو الاستجابة لطلبات dbus من أي مستخدم، مما يمكِّنه من إدارة مجموعات التحكم التي أُسنِدَت إليه فقط. لمحة إن مجموعات التحكم (cgroups) هي الميزة التي تستعمل لتجميع المهام؛ حيث يكون تتبع الموارد ووضع حدود لها مُدارًا من أنظمة فرعية؛ إذ أنَّ الهيكلية (hierarchy) هي مجموعة من الأنظمة الفرعية الموصولة مع بعضها بعضًا؛ على سبيل المثال، إذا كانت الأنظمة الفرعية للذاكرة والأجهزة (devices) موصولة مع بعضها في ‎/sys/fs/cgroups/set1، فيمكن لأي مهمة في ‎/child1 أن تكون عرضةً للحدود الموافقة للنظامين الفرعيين السابقين. حيث تُشكِّل كل مجموعة من الأنظمة الفرعية الموصولة «هيكليةً» (مع استثناءات)؛ مجموعات التحكم التي تكون أولاد ‎/child1 تكون عرضةً للحدود المفروضة على ‎/child1، ويكون استهلاك الموارد محسوبًا على ‎/child1. الأنظمة الفرعية الموجودة تتضمن: cpusets: تبسيط إسناد مجموعة من المعالجات وعُقَد الذاكرة إلى مجموعات التحكم؛ فالمهام في مجموعة تحكّم فيها النظام الفرعي cpusets يمكن أن تستخدم المعالجات المُسنَدة إلى تلك المجموعة فقط. blkio: تحديد كتل الدخل/الخرج لكل مجموعة تحكم. cpuacct: توفير حساب الاستهلاك للمعالج لكل مجموعة تحكم. devices: التحكم في قدرة المهام على إنشاء أو استخدام عقد الأجهزة إما باستعمال قائمة بيضاء (whitelist) أو سوداء (blacklist). freezer: توفير طريقة «لتجميد» (freeze) و «تذويب» (thaw) مجموعات التحكم؛ لا يمكن جدولة (scheduled) مجموعات التحكم وهي مجمدة. hugetlb: تبسيط وضع حد لاستهلاك hugetlb لكل مجموعة تحكم. memory: السماح للذاكرة، وذاكرة النواة، وذاكرة التبديل (swap) بأن تُتَبَّع وتقيّد. net_cls: توفير واجهة لوضع علامات على الرزم الشبكية بناءً على مجموعة التحكم المُرسِلة؛ يمكن استعمال هذه العلامات لاحقًا باستخدام tc‏ (traffic controller) لإسناد أولويات للرزم الشبكية. net_prio: السماح بضبط أولوية بيانات التراسل الشبكي بناءً على مجموعة التحكم. cup: تمكين ضبط جدولة الخصائص على أساس مجموعة التحكم. pref_event: تفعيل نمط لكل معالج لمراقبة الخيوط (threads) لمجموعات تحكم معينة. يمكن إنشاء مجموعات تحكم مُسماة دون استخدام أنظمة فرعية معها، ويكون الغرض من ذلك هو تتبع العمليات؛ على سبيل المثال، يقوم systemd بذلك لتتبع خدماته وجلسات المستخدم. نظام الملفات تُنشَأ هيكلية بوصل نسخة من نظام ملفات مجموعة التحكم لكل نظام فرعي مُراد استخدامه كخيار للوصل؛ على سبيل المثال: mount -t cgroup -o devices,memory,freezer cgroup /cgroup1 وهذا ما سيُنشِئ هيكلية فوريًا مع الأجهزة ومجموعات التحكم للذاكرة موصولةً مع بعضها؛ ويمكن إنشاء مجموعة تحكم فرعية (child cgroup) باستخدام mkdir: mkdir /cgroup1/child1 يمكن نقل المهام إلى مجموعة التحكم الفرعية الجديدة بكتابة أرقام معرفات عملياتهم في ملف tasks أو cgroup.procs: sleep 100 echo $! > /cgroup1/child1/cgroup.procs يمكن الإدارة أيضًا عبر ملفات في مجلدات cgroup؛ على سبيل المثال، لتجميد جميع المهام في child1: echo FROZEN > /cgroup1/child1/freezer.state يمكن العثور على كمية كبيرة من المعلومات عن مجموعات التحكم وأنظمتها الفرعية في مجلد التوثيق cgroups في شجرة مصدر النواة. التفويض يمكن لملفات ومجلدات مجموعات التحكم أن تُملَك من مستخدمين غير المستخدم الجذر، مما يمكِّن تفويض (delegation) إدارة مجموعات التحكم؛ عمومًا، تُجبِر النواة القيود المفروضة على الهيكلية على الأولاد؛ على سبيل المثال، إن كانت مجموعة الأجهزة ‎/child1 لا تملك وصولًا للقرص الصلب، فلا تستطيع مجموعة التحكم ‎/child1/child2 إعطاء نفسها هذه الامتيازات. في أوبنتو 14.04، يوضع المستخدمون افتراضيًا في مجموعة من مجموعات التحكم التي يملكونها، مما يسمح لهم باحتواء المهام التي يشغلونها باستخدام مجموعات تحكم فرعية بأمان؛ تُستخدَم هذه الميزة عمليًا ويمكن الاعتماد عليها فمثلًا يمكن استخدامها لإنشاء حاوية LXC دون امتيازات. المدير مدير مجموعات التحكم (cgmanager) يوفر خدمة D-Bus للسماح للبرامج والمستخدمين بإدارة مجموعات التحكم دون الحاجة إلى معرفة أو وصول مباشر إلى نظام ملفات مجموعات التحكم. وللطلبات من المهام في نفس مجال الأسماء (namespace) للمدير، فيمكن للمدير إجراء التحققات الأمنية اللازمة للتأكد من شرعية تلك الطلبات؛ وللطلبات الأخرى، كتلك القادمة من مهمة في حاوية، فيجب القيام بطلبات D-Bus مُحَسَّنة؛ حيث يجب أن تُمرَّر معرفات process، و user، و group على شكل SCM_CREDENTIALS، لذلك يمكن للنواة ربط المعرفات إلى قيم المضيف العامة. ولتبسيط استخدام استدعاءات D-Bus من جميع المستخدمين، فيبدأ «وسيط مدير مجموعات التحكم» (cgproxy) تلقائيًا في الحاويات؛ حيث يقبل طلبات D-Bus قياسية من المهام في نفس مجال أسمائه، ثم يحوله إلى طلبات SCM D-Bus محسنة التي تُمرَّر بعد ذلك إلى cgmanager. مثال بسيط عن إنشاء مجموعة تحكم -التي ستُشغِّل تصريفًا (compile) يستهلك كثيرًا من طاقة المعالجة- سيكون كالآتي: cgm create cpuset build1 cgm movepid cpuset build1 $$ cgm setvalue cpuset build1 cpuset.cpus 1 make مصادر مشروع cgmanager مُستضاف في linuxcontainers.org. يمكن العثور على صفحة توثيق النواة هنا. ويمكن العثور على دليل freedesktop.org لاستخدام مجموعات التحكم هنا. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: Control Groups.
  22. تعرفنا في الدرسين الماضيين عن مفهوم حاويات لينكس (LXC) ومبدأ عملها ثم شرعنا في كيفية البدء في استعمالها. سنعرج في هذا الدرس إلى كيفية استغلال مختلف نقاط ومراحل دورة حياة حاويات لينكس (LXC) لإدراج بعض من الإضافات (hooks) التي تقوم بإجراء مهمة ما. إضافات إدارة دورة التشغيل بدءًا من أوبنتو 12.10، أصبح من الممكن تعريف إضافات (hooks) تُنفَّذ عند نقاط محددة من دورة تشغيل الحاوية: الإضافات التي تحدث قبل التشغيل تُنفَّذ من مجال أسماء المضيف قبل أن تُنشَأ طرفيات أو نقاط وصل الحاويات؛ إذا أُجري أي وصل في هذه الفترة، فيجب أن يُنظَّف في إضافة تحدث بعد إيقاف التشغيل. الإضافات التي تحدث قبل الوصل تُنفَّذ في مجال أسماء الحاوية، لكن قبل أن يوصل جذر نظام الملفات؛ سينظف أي وصل لنظام الملفات في هذه الفترة تلقائيًا عند إيقاف تشغيل الحاوية. إضافات الوصل هي إضافات تنفذ بعد وصل أنظمة ملفات الحاوية، لكن قبل أن تُنفِّذ الحاوية pivot_root لتغيير جذر نظام ملفاتها. الإضافات التي تحدث بعد إيقاف التشغيل ستنفَّذ بعد إيقاف تشغيل الحاوية. إذا أعادت أيّة إضافة خطأً، فسيلغى تشغيل الحاوية، لكن أي إضافة تحدث بعد إيقاف التشغيل ستنفَّذ، ستُسجَّل أيّة مخرجات تولد من السكربت بأولوية التنقيح (debug). رجاءً راجع صفحة دليل lxc.container.conf لصيغة ملف الضبط التي سيحدد الإضافات؛ يمكن أن تأتي بعض أمثلة الإضافات في الحزمة lxc لتخدم كمثال حول طريقة كتابة إحدى تلك الإضافات. سطر الأوامر لدى الحاويات عدد مضبوط من «أسطر الأوامر» (consoles)؛ أحدها موجودٌ دائمًا في ‎/dev/console؛ الذي يظهر في الطرفية عندما تُشغِّل lxc-start ما لم تحدد الخيار ‎-d؛ يمكن إعادة توجيه ناتج خرج ‎/dev/console إلى ملف باستخدام ‎-c console-file في الأمر lxc-start؛ يمكن تحديد عدد إضافي من أسطر الأوامر باستخدام المتغير lxc.tty المضبوط عادةً إلى 4؛ يمكن أن تظهر أسطر الأوامر تلك في ‎/dev/ttyN (حيث N أكبر أو تساوي 1، وأصغر أو تساوي 4)؛ ولتسجيل الدخول إلى console 3 من المضيف، فنفِّذ الأمر: sudo lxc-console -n container -t 3 إذا لم تحدد الخيار ‎-t N، فسيتم اختيار سطر أوامر غير مُستخدَم؛ للخروج منه، استخدام عبارة الخروج Ctrl-a q؛ لاحظ أن عبارة الخروج لا تعمل في سطر الأوامر الناتج عن lxc-start دون الخيار ‎-d. استكشاف الأخطاء التسجيل إذا حدث شيء ما خاطئ عند تشغيل حاوية، فإن أول خطوة هي الحصول على سجل كامل من LXC: sudo lxc-start -n C1 -l trace -o debug.out هذا سيؤدي إلى جعل lxc يسجل في أعلى درجة إسهاب، التي هي trace، وسيكون ملف التخزين هو ملف باسم «debug.out»، إذا كان الملف debug.out موجودًا مسبقًا، فستُضاف معلومات السجل الجديد إليه. مراقبة حالة الحاوية هنالك أمران متوفران لمراقبة تغيرات حالة الحاوية: lxc-monitor الذي يراقب حاويةً أو أكثر ﻷي تغيرات في الحالة، حيث يأخذ اسم الحاوية مع الخيار ‎-n كالعادة؛ لكن في هذا الحالة، يمكن أن يكون اسم الحاوية تعبيرًا نمطيًا من نمط POSIX للسماح بمراقبة مجموعة من الحاويات؛ يستمر lxc-monitor بالعمل ويعرض تغيرات حالات الحاويات؛ أما lxc-wait فينتظر تغيِّرًا محددًا في الحالة ثم ينتهي تنفيذه؛ على سبيل المثال: sudo lxc-monitor -n cont[0-5]* هذا سيعرض جميع تغيرات الحالة لأي حاوية تطابق التعبير النمطي؛ بينما: sudo lxc-wait -n cont1 -s 'STOPPED|FROZEN' سينتظر إلى أن تتغير حالة الحاوية cont1 إلى STOPPED أو FROZEN ثم ينتهي. الوصل من الممكن في أوبنتو 14.04 الوصل (attach) إلى مجال أسماء حاوية، أبسط طريقة هي تنفيذ: sudo lxc-attach -n C1 الذي سيبدأ صدفة موصولة لمجال الحاوية C1، أو داخل الحاوية؛ آلية عمل الوصل هي معقدة جدًا، مما يسمح بوصل مجموعة فرعية من مجالات أسماء (namespaces) الحاوية ونمط الحماية (security context)، راجع صفحة الدليل لمزيدٍ من المعلومات. درجة إسهاب init في الحاوية إذا أكمل LXC بدء تشغيل الحاوية، لكن فشل إكمال تنفيذ init فيها (على سبيل المثال، لم يُعرَض محث الدخول)، فمن المفيد طلب درجة إسهاب أكبر من عملية init، فلحاوية upstart: sudo lxc-start -n C1 /sbin/init loglevel=debug يمكنك أيضًا بدء تشغيل برامج مختلفة عن init، على سبيل المثال: sudo lxc-start -n C1 /bin/bash sudo lxc-start -n C1 /bin/sleep 100 sudo lxc-start -n C1 /bin/cat /proc/1/status واجهة LXC البرمجية API يمكن الوصول إلى غالبية وظائف LXC عبر واجهة برمجية (API) مُصدَّرة من liblxc التي تكون ارتباطاتها متوفرة لعدة لغات برمجية بما فيها بايثون، و lua، وروبي، و go. ما يلي هو مثال عن استخدام ربط بايثون (المتوفرة في حزمة python3-lxc)، التي تُنشِئ وتبدأ حاوية، ثم تنتظر إلى أن يوقف تشغيلها: # sudo python3 Python 3.2.3 (default, Aug 28 2012, 08:26:03) [GCC 4.7.1 20120814 (prerelease)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import lxc __main__:1: Warning: The python-lxc API isn't yet stable and may change at any p oint in the future. >>> c=lxc.Container("C1") >>> c.create("ubuntu") True >>> c.start() True >>> c.wait("STOPPED") True الحماية يربط مجال الأسماء المعرفات (ids) إلى الموارد؛ لكنه لا يوفر للحاوية أي معرِّف يمكنه أن يشير إلى المورد، لذلك يمكن أن يُحمى المورد؛ وهذا هو أساس بعض الحماية الموفرة لمستخدمي الحاوية؛ على سبيل المثال، مجال أسماء IPC معزول تمامًا؛ لكن مجالات أسماء أخرى فيها بعض «التسربات» (leaks) التي تسمح للامتيازات بأن تُستخرَج بشكل غير ملائم من الحاوية إلى حاوية أخرى، أو إلى المضيف. افتراضيًا، تُشغَّل حاويات LXC بسياسة AppArmor التي تقيّد بعض الأفعال، تفاصيل دمج AppArmor مع LXC موجودة في قسم «AppArmor» في الدرس السابق، الحاويات دون امتيازات تربط الجذر في الحاوية إلى مستخدم دون امتيازات في المضيف، وهذا يمنع الوصول إلى ملفات ‎/proc و ‎/sys التي تمثل موارد المضيف، وغيرها من الملفات المملوكة من الجذر في المضيف. الثغرات في استدعاءات النظام ميزة أساسية من مزايا الحاويات أنها تشارك النواة مع المضيف؛ وهذا يعني أنه إذا حوت النواة على أيّة ثغرات في استدعاءات النظام (system calls)، فيمكن أن تستغلها الحاوية؛ وبعد أن تتحكم حاوية بالنواة، فيمكنها أن تسيطر سيطرةً كاملةً على أي مورد معروف للمضيف! بدءًا من أوبنتو 12.10، يمكن أن تقيَّد الحاوية من مرشِّح seccomp، إن Seccomp هو ميزة جديدة في النواة التي تُرشِّح استدعاءات النظام التي يمكن أن تُستخدَم من المهمة وأولادها؛ بينما يتوقع الوصول إلى إدارة سهلة ومحسنة للسياسة في المستقبل القريب، لكن تحتوي السياسة الحالية على قائمة بيضاء بسيطة لأرقام استدعاءات النظام؛ يبدأ ملف السياسة برقم الإصدار (الذي يجب أن يكون 1) في أول سطر ونوع السياسة (الذي يجب أن يكون whitelist) في ثاني سطر؛ وتُلحَق بقائمة أرقام، كل رقم في سطر. سنحتاج عادةً لتشغيل حاوية بتوزيعة كاملة إلى عدد كبير من استدعاءات النظام؛ لكن لحاويات البرامج، يمكن أن نقلل عدد استدعاءات النظام المتوفرة إلى رقم قليل؛ وحتى للحاويات التي تشغل توزيعات كاملة يمكن الحصول على فوائد أمنية إذا حذفت -على سبيل المثال- استدعاءات النظام المتوافقة مع 32 بت في حاوية 64 بت؛ راجع صفحة دليل lxc.container.conf للمزيد من التفاصيل حول كيفية ضبط الحاوية لتستخدم seccomp؛ لن تُحمَّل افتراضيًا سياسة seccomp. مصادر كتاب «Secure Containers Cookbook» يشرح كيفية استخدام أنماط الحماية لجعل الحاويات أكثر أمانًا. مشروع LXC مُستضاف في linuxcontainers.org. مشاكل LXC الأمنية مذكورة ومناقشة في صفحة وكي «LXC Security». ترجمة -وبتصرف- للمقال Ubuntu Server Guide: LXC.
  23. تعرفنا في الدرس الماضي على مفهوم حاويات لينكس (LXC)، مبدأ عملها وكيفية تثبيتها، سنشرع في هذا الدرس إلى كيفية بدء تشغيلها واستعمالها. لا يملك LXC عفريتًا (daemon) يعمل طوال الوقت، لكنه يملك مهام upstart: المهمة ‎/etc/init/lxc-net.conf: هي مهمة اختيارية تعمل فقط إذا حَدَّد الملف ‎/etc/default/lxc الخاصية USE_LXC_BRIDGE (قيمتها هي true افتراضيًا)؛ حيث تهيِّء جسر NAT لكي تستخدمه الحاويات. المهمة ‎/etc/init/lxc.conf: تعمل إذا كانت الخاصية LXC_AUTO (قيمتها true افتراضيًا) مضبوطة إلى true في ‎/etc/default/lxc؛ حيث تبحث عن القيود في المجلد ‎/etc/lxc/auto‎/‎ حيث توجد وصلات رمزية إلى ملفات الضبط للحاويات التي يجب أن تُشغَّل في وقت الإقلاع. المهمة ‎/etc/init/lxc-instance.conf: تُستخدَم من ‎/etc/init/lxc.conf للبدء التلقائي لتشغيل حاوية. التخزين يدعم LXC عدّة أنماط من التخزين لجذر نظام ملفات الحاوية؛ افتراضيًا يكون مجلدًا بسيطًا، لأنه لا يتطلب أي ضبط مسبق للمضيف طالما أن نظام الملفات فيه مساحة تخزينية كافية؛ وهو لا يتطلب أيضًا امتيازات الجذر لإنشاء المخزن، لذلك سيكون ملائمًا للاستخدام دون امتيازات؛ جذر نظام الملفات للاستخدام مع امتيازات موجود افتراضيًا في ‎/var/lib/lxc/C1/rootfs، بينما جذر نظام الملفات للحاويات التي تعمل دون امتيازات يكون في ‎~/.local/share/lxc/C1/rootfs، إذا حُدِّد lxcpath خاص في lxc.system.com، فإن جذر نظام ملفات الحاوية سيكون موجودًا في ‎$lxcpath/C1/rootfs. نسخة snapshot باسم C2 لحاوية C1 التي تُخزَّن في مجلد ستصبح حاوية overlayfs، بجذر نظام ملفات هو overlayfs:/var/lib/lxc/C1/rootfs:/var/lib/lxc/C2/delta0، أنواع التخزين الأخرى تتضمن loop، و btrfs، و LVM، و zfs. حاوية تعتمد على تخزين btrfs تبدو عمومًا مثل حاوية تعتمد على التخزين في مجلد، ويكون جذر نظام الملفات في نفس المكان؛ لكن جذر نظام الملفات يحتوي على حجم فرعي (subvolume)، لذلك تكون نسخة snapshot مُنشَأة باستخدام نسخة snapshot لحجم فرعي. جذر نظام الملفات لحاوية تستخدم LVM يمكن أن يكون أي حجم منطقي منفصل؛ اسم مجموعة الحجوم الافتراضي يمكن أن يُحدَّد في ملف lxc.conf؛ ويُضبَط نوع وحجم نظام الملفات لكل حاوية باستخدام lxc-create. جذر نظام الملفات لحاوية تستخدم zfs هو نظام ملفات zfs منفصل، وموصول في المكان التقليدي ‎/var/lib ‎/lxc/C1/rootfs، يمكن تحديد zfsroot باستخدام lxc-create، ويمكن تحديد قيمة افتراضية في ملف lxc.system.conf. المزيد من المعلومات حول إنشاء الحاويات بمختلف طرائق التخزين يمكن أن توجد في صفحة دليل lxc-create. القوالب يتطلب إنشاء حاوية عادةً إنشاء جذر نظام ملفات للحاوية؛ يفوض الأمر lxc-create هذا العمل إلى القوالب (templates)، التي تكون عادةً خاصة بالتوزيعة؛ قوالب lxc التي تأتي مع lxc يمكن أن توجد في مجلد ‎/usr/share/lxc/templates، بما فيها القوالب لإنشاء أوبنتو، ودبيان، وفيدورا، وأوراكل، وسنتوس، وجنتو بالإضافة لغيرها. إنشاء صور للتوزيعات في أغلب الحالات يتطلب القدرة على إنشاء عقد أجهزة، ويتطلب ذلك أدوات التي ليست متوفرة في بقية التوزيعات، وعادةً يستغرق هذا الأمر وقتًا طويلًا؛ فلذلك يأتي lxc بقالب download، الذي ينزل صور مبنية مسبقًا للحاويات من خادوم lxc مركزي؛ أهم حالة استخدام هي السماح بإنشاء بسيط لحاويات دون امتيازات بواسطة مستخدمين غير الجذر، الذين لن يستطيعوا ببساطة تشغيل الأمر debootstrap. عند تشغيل lxc-create، فجميع الخيارات التي تأتي بعد «--» تُمرَّر إلى القالب؛ ففي الأمر الآتي، تمرر الخيارات ‎--name و ‎--template و ‎--bdev إلى lxc-create، بينما يمرر الخيار ‎--release إلى القالب: lxc-create --template ubuntu --name c1 --bdev loop -- --release trusty يمكنك الحصول على مساعدة حول الخيارات المدعومة في حاوية معينة بتمرير الخيار ‎--help واسم القالب إلى الأمر lxc-create؛ فعلى سبيل المثال، للحصول على مساعدة حول تنزيل قالب: lxc-create --template download --help البدء التلقائي يدعم LXC تعليم الحاويات لكي تُشغَّل عند إقلاع النظام؛ ففي الإصدارات قبل أوبنتو 14.04، كان يتم ذلك باستخدام وصلات رمزية في المجلد ‎/etc/lxc/auto؛ وبدءًا من أوبنتو 14.04، يتم ذلك عبر ملفات ضبط الحاوية؛ القيد: lxc.start.auto = 1 lxc.start.delay = 5 يعني أن على الحاوية البدء عند إقلاع النظام ويجب الانتظار 5 ثواني قبل بدء تشغيل الحاوية التالية؛ يدعم LXC أيضًا ترتيب وتجميع الحاويات، وأيضًا إعادة الإقلاع وإيقاف التشغيل عبر مجموعات autostart؛ راجع صفحات دليل lxc-autostart و lxc-container.conf للمزيد من المعلومات. AppArmor يأتي LXC مع ملف ضبط AppArmor مهمته هي حماية المضيف من الإساءة العرضية للامتيازات داخل الحاوية؛ على سبيل المثال، لن تكون الحاوية قادرةً على الكتابة إلى ‎/proc/sysrq-trigger أو أغلبية ملفات ‎/sys. الملف usr.bin.lxc-start يدخل حيز التنفيذ عند تشغيل lxc-start؛ يمنع ملف الضبط lxc-start من وصل أنظمة ملفات جديدة خارج نظام ملفات الجذر الخاص بالحاوية؛ قبل تنفيذ init للحاوية، فإن LXC يطلب تبديلًا لملف ضبط الحاوية؛ افتراضيًا، هذا الضبط هو السياسة lxc-container-default المعرَّفة في ملف الضبط ‎/etc/apparmor.d/lxc/lxc-default. يمنع هذا الضبط الحاوية من الوصول إلى مسارات خطرة، ومن وصل أغلبية أنظمة الملفات. لا يمكن تقييد البرامج في الحاوية أكثر من ذلك؛ فعلى سبيل المثال، خادوم MySQL الذي يعمل ضمن نطاق الحاوية (مما يحمي المضيف) لا يمكن أن يدخل في نطاق ملف ضبط MySQL (لحماية الحاوية). لا يدخل lxc-execute ضمن سلطة AppArmor، لكن الحاوية التي يُنشِئها (spawn) ستكون مقيدةً. تعديل سياسات الحاوية إذا وجدت أن lxc-start لا يعمل بسبب تقييد في الوصول من سياسة AppArmor، فيمكنك تعطيل ملف ضبط lxc-start بتنفيذ: sudo apparmor_parser -R /etc/apparmor.d/usr.bin.lxc-start sudo ln -s /etc/apparmor.d/usr.bin.lxc-start /etc/apparmor.d/disabled/ هذا سيجعل lxc-start يعمل دون قيود، لكن ستبقى الحدود موجودةً للحاوية نفسها، وإذا أردت إزالة التقييد عن الحاوية، فعليك بالإضافة إلى تعطيل ملف الضبط usr.bin.lxc-start أن تضيف السطر: lxc.aa_profile = unconfined إلى ملف ضبط الحاوية. يأتي LXC مع سياسات بديلة للحاويات، فإذا أردت إنشاء حاويات داخل حاويات (تشعب)، فعليك استخدام ملف الضبط lxc-container-default-with-nasting بإضافة السطر الآتي إلى ملف ضبط الحاوية: lxc.aa_profile = lxc-container-default-with-nesting إذا أردت استخدام libvirt داخل الحاويات، فستحتاج إلى تعديل تلك السياسة (المعرفة في ‎‎/etc/apparmor.d/lxc/lxc-default-with-nasting) وإزالة التعليق عن السطر الآتي: mount fstype=cgroup -> /sys/fs/cgroup/**, ثم أعد تحميل السياسة. لاحظ أن سياسة التشعب للحاويات ذات الامتيازات هي أقل أمانًا من السياسة الافتراضية، حيث تسمح للحاويات بإعادة وصل ‎/sys و ‎/proc في أمكان غير قياسية، مما يتجاوز سياسة AppArmor؛ لا تملك الحاويات دون امتيازات هذا التأثير الجانبي، ﻷن جذر الحاوية لا يمكنه الكتابة إلى ملفات proc و sys المملوكة من الجذر. إذا أردت تشغيل الحاوية بملف ضبط مخصص، فبإمكانك إنشاء ملف ضبط في ‎/etc/apparmor.d/lxc، ويجب أن يبدأ اسمه بالكلمة lxc-‎ لكي يُسمَح لبرنامج lxc-start بالانتقال إليه؛ ملف lxc-default يتضمن إعادة استعمال الملف المجرد ‎/etc/apparmor.d/abstraction/lxc/container-base؛ طريقة سهلة لإنشاء ملف ضبط جديد هي فعل المثل، ثم إضافة الأذونات الإضافية في نهاية السياسة. حَمِّل الضبط الجديد بعد إنشاءه كما يلي: sudo apparmor_parser -r /etc/apparmor.d/lxc-containers سيُحمَّل هذا الضبط تلقائيًا بعد إعادة الإقلاع، ﻷنه يُقرَأ من الملف ‎/etc/apparmor.d/lxc-containers؛ وفي النهاية ولجعل الحاوية CN تستخدم ملف الضبط الجديد lxc-CN-profile، فأضف السطر الآتي إلى ملف الضبط: lxc.aa_profile = lxc-CN-profile مجموعات التحكم إن مجموعات التحكم (cgroups) هي ميزة من ميزات النواة توفر تجميع للمهام تجميعًا هيكليًا، وإسناد وتحديد الموارد لكل مجموعة تحكم؛ تُستخدَم في الحاويات للحد من الوصول إلى الأجهزة الكتلية أو المحرفية (block or character devices) وتجمِّد عمل الحاويات؛ يمكن استعمالها أيضًا لتحديد استخدام الذاكرة وإيقاف الدخل أو الخرج، وضمانة استخدام أصغري للمعالج، والسماح للحاوية بالوصول إلى معالجات محددة. افتراضيًا، سيُسند للحاوية CN ذات امتيازات مجموعةُ تحكمٍ باسم ‎/lxc/CN؛ وفي حال حدوث تضارب بالاسم (الذي قد يحدث عند استخدام lxcpaths مخصصة)، فستُضاف لاحقة «‎-n» حيث n هو رقم صحيح يبدأ من الصفر، ويُسنَد إلى اسم مجموعة التحكم. افتراضيًا، سيُسند للحاوية CN دون امتيازات مجموعة تحكم باسم CN في مجموعة التحكم الخاصة بالمهمة التي بدأت الحاوية، على سبيل المثال ‎/usr/1000.user/1.session/CN سيُمنَح جذر الحاوية ملكية المجموعة للمجلد (لكن ليس جميع الملفات)، وهذا ما سيسمح بإنشاء مجموعات تحكم فرعية. وفي أوبنتو 14.04، يستخدم LXC مدير مجموعات التحكم cgmanager لإدارة مجموعات التحكم؛ يستقبل مدير مجموعات التحكم طلبات D-Bus عبر مقبس يونكس ‎/sys/fs/cgroup/cgmanager/sock؛ يجب أن يُضاف السطر الآتي لاستخدام آمن للحاويات المتشعبة: lxc.mount.auto = cgroup إلى ملف ضبط الحاوية، مما يصل المجلد ‎/sys/fs/cgroup/cgmanager وصلًا ترابطيًا (bind-mounted) إلى الحاوية؛ ويجب على الحاوية في المقابل تشغيل وسيط إدارة مجموعات التحكم (ويتم ذلك افتراضيًا إذا كانت الحزمة cgmanager مثبتةً على الحاوية) الذي سينقل المجلد ‎/sys/fs/cgroup/cgmanager إلى ‎/sys/fs/cgroup‎/cgmanager.lower ثم سيبدأ الاستماع إلى الطلبات للوسيط على مقبسه ‎/sys/fs/cgroup ‎/cgmanager‎/sock؛ سيتأكد مدير مجموعات التحكم في المضيف أن الحاويات المتشعبة لن تستطيع «الهروب» من مجموعات التحكم المُسندَة إليها أو إنشاء طلبات غير مصرح لها بها. الاستنساخ للتزويد السريع بالحاويات، ربما تريد تخصيص حاوية تبعًا لحاجاتك ثم تُنشِئ عدَّة نسِخٍ منها؛ ويمكن فعل ذلك بالبرنامج lxc-clone. الاستنساخ إما أن يكون عبر snapshots أو بنسخ حاوية أخرى؛ فالنسخ هو إنشاء حاوية جديدة منسوخة من الأصلية، وتأخذ مساحة تخزينية مثل الحاوية الأصلية؛ أما snapshot فإنها تستخدم قدرة آلية التخزين على إنشاء snapshots لإنشاء حاوية النسخ-عند-الكتابة (copy-on-write) تُشير إلى الحاوية الأولى؛ يمكن إنشاء snapshots للحاويات المخزنة في btrfs، و LVM، و zfs، وتلك التي تكون مخزنة في مجلدات؛ حيث كل آلية تخزين لها خصوصياتها؛ فمثلًا، حاويات LVM التي ليست thinpool-provisioned لا تدعم إنشاء snapshots من snapshots؛ ولا يمكن حذف حاويات zfs مع snapshots قبل أن تُطلَق (release) جميع snapshots؛ ويجب أن يُخطط جيدًا لحاويات LVM فقد لا يدعم نظام الملفات أن يزيد حجمه. لا يعاني btrfs من تلك السلبيات، لكنه يعاني من أداء fsync منخفض يسبب جعل dpkg و apt-get أبطئ. تُنشَأ snapshots من الحاويات المخزنة في مجلدات عبر نظام الملفات؛ فمثلًا يكون لحاوية ذات امتيازات C1 جذر نظام ملفات في ‎/var/lib/lxc/C1/rootfs، وستبدأ نسخة snapshot للحاوية C1 باسم C2 بجذر نظام الملفات للحاوية C1 موصولًا للقراءة فقط في ‎/var/lib/lxc/C2/delta0؛ كل ما يهم في هذه الحالة أنه لا يفترض أن تعمل أو تحذف الحاوية C1 أثناء عمل C2؛ من المستحسن اعتبار الحاوية C1 هي حاوية أساسية واستخدام نسخة snapshot لها فقط. لنفترض أن لدينا حاوية باسم C1، فيمكن إنشاء نسخة منها باستخدام الأمر: sudo lxc-clone -o C1 -n C2 يمكن إنشاء snapshot باستخدام: sudo lxc-clone -s -o C1 -n C2 راجع صفحة دليل lxc-clone لمزيد من المعلومات. Snapshots LXC يدعم snapshots لتسهيل دعم نسخ snapshot لتطوير تكراري للحاوية؛ فعندما تعمل على حاوية C1 -وقبل إنشاء تغيير خطير وصعب العكس- يمكنك إنشاء snapshot: sudo lxc-snapshot -n C1 التي هي نسخة snapshot باسم «snap0» في مجلد ‎/var/lib/lxcsnaps أو ‎$HOME/.local ‎/share/lxcsnaps، النسخة الثانية ستُسمى «snap1» وهكذا؛ يمكن عرض النسخ الموجودة حاليًا باستخدام الأمر lxc-snapshot -L -n C1، ويمكن أن تُستعاد نسخة snapshot وتمحى حاوية C1 الحالية باستخدام الأمر lxc-snapshot -r snap1 -n C1، وبعد تنفيذ أمر الاستعادة، فستبقى النسخة snap1 موجودةً. تُدعَم snapshots لحاويات btrfs، و lvm، وzfs، و overlayfs؛ إذا استدعي الأمر lxc-snapshot على حاوية تُخزَّن في مجلد، فسيسجل خطأ وستُنشَأ نسخة copy-clone؛ وسبب ذلك أنه لو أنشأ المستخدم نسخة overlayfs snapshot لحاوية تخزن في مجلد، فسينعكس جزء من تغيرات الحاوية الأصلية على نسخة snapshot؛ إذا كنت تريد إنشاء snapshots لحاوية C1 مخزنة في مجلد، فيمكن إنشاء نسخة overlayfs للحاوية C1، ويجب ألّا تلمس C1 بعد ذلك قط، لكن يمكن أن نعدِّل overlayfs وننسخها نسخ snapshots كما نريد، أي: lxc-clone -s -o C1 -n C2 lxc-start -n C2 -d # make some changes lxc-stop -n C2 lxc-snapshot -n C2 lxc-start -n C2 # etc الحاويات العابرة «الحاويات العابرة» (Ephemeral containers) هي حاويات تستخدم لمرة واحدة فقط؛ فليكن لدينا حاوية موجودة مسبقًا باسم C1، فيمكنك إنشاء حاوية عابرة باستخدام: lxc-start-ephemeral -o C1 ستبدأ الحاوية كنسخة snapshot للحاوية C1، وستطبع التعليمات للدخول إلى الحاوية على الطرفية، وستدمر الحاوية العابرة بعد إيقاف التشغيل، راجع صفحة الدليل lxc-start-ephemeral لمزيد من الخيارات. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: LXC.
  24. الحاويات (containers) هي تقنية أنظمة وهمية خفيفة؛ حيث تجنح لأن تكون شبيهةً بطريقة chroot محسّنة بدلًا من كونها تقنية أنظمة وهمية كاملة مثل Qemu أو VMware؛ لأن كلاهما لا يحاكي العتاد ولأن الحاويات تشارك نفس نظام التشغيل للمضيف؛ لذلك من الأفضل مقارنة الحاويات إلى «نطاقات سولارس» (Solaris zones) أو «سجون BSD» ‏(BSD jails). إن Linux-vserver و OpenVZ هما نسختان من الحاويات لنظام لينُكس مطورتان بشكل منفصل عن بعضهما؛ في الواقع، ظهرت الحاويات نتيجةً للعمل على تطوير وظائف vserver و OpenVZ. هنالك نسختان في «مجال المستخدم» (user-space) للحاويات تستخدمان نفس مزايا النواة؛ تسمح Libvirt باستخدام الحاويات عبر محرك LXC بالاتصال إلى «lxc:///‎»، قد يكون هذا أمرًا ملائمًا لأنها تملك نفس طريقة الاستخدام الموجودة في المحركات الأخرى. النسخة الأخرى المُسماة ببساطة «LXC» هي غير متوافقة مع libvirt؛ لكنها أكثر مرونةً بأدوات أكثر في مجال المستخدم؛ من الممكن التبديل بين النسختين آنفتَيّ الذكر، لكن هنالك بعض الخصوصيات التي قد تسبب ارتباكًا. سنشرح في هذا الدرس حزمة lxc شرحًا رئيسيًا، حيث أن استخدام libvirt-lxc ليس مستحسنًا ﻷنه يفتقر إلى حماية AppArmor لحاويات libvirt-lxc؛ وستكون أسماء الحاويات الموجودة في هذا الفصل هي CN، أو C1، أو C2. التثبيت يمكن تثبيت حزمة lxc باستخدام الأمر: sudo apt-get install lxc سنحتاج إلى تنزيل الاعتماديات المطلوبة والمستحسنة، وضبط جسر الشبكة لكي يستخدمه الحاويات؛ إذا أردت استخدام حاويات دون امتيازات، فربما تحتاج إلى أن تتأكد أن للمستخدمِين امتيازات subuids و subgids، وتريد أن تسمح للمستخدمين بوصل الحاويات إلى جسر؛ راجع القسم «الاستخدام الأساسي دون امتيازات». الاستخدام الأساسي يمكن أن نستخدم LXC بطريقتين مختلفتين، الأولى بامتيازات عبر تنفيذ أوامر lxc بحساب المستخدم الجذر؛ أو دون امتيازات بتنفيذ أوامر lxc بحساب أي مستخدم عدا الجذر (في الواقع، يمكن تشغيل حاويات دون امتيازات بحساب الجذر، لكننا لن نشرح ذلك هاهنا)؛ الحاويات دون امتيازات محدودة أكثر، فمثلًا لن تستطيع إنشاء عقد أجهزة أو تصل أنظمة ملفات كتلية؛ لكنها أقل خطرًا للمضيف، حيث يكون الجذر في الحاوية مربوطًا بحساب غير جذر في المضيف. الاستخدام الأساسي بامتيازات لإنشاء حاوية ذات امتيازات، كل ما عليك فعله هو تنفيذ الأمر: sudo lxc-create --template download --name u1 أو بشكل مختصر: sudo lxc-create -t download -n u1 الذي سيسألك تفاعليًا عن نوع جذر نظام الملفات لكي يُنزَّل، وخصوصًا التوزيعة والإصدارة والمعمارية؛ يمكنك تحديد هذه القيم في سطر الأوامر لإنشاء حاوية دون الإجابة على تلك الأسئلة تفاعليًا: sudo lxc-create -t download -n u1 -- --dist ubuntu --release trusty --arch amd64 أو sudo lxc-create -t download -n u1 -- -d ubuntu -r trusty -a amd64 يمكنك الآن استخدام lxc-ls لعرض قائمة بالحاويات، و lxc-info للحصول على معلومات مفصلة عن حاوية، و lxc-start لبدء و lxc-stop لإيقاف الحاوية؛ بينما يسمح لك الأمران lxc-attach و lxc-console بالدخول إلى حاوية إذا لم يكن الاتصال إليها عبر SSH متاحًا؛ والأمر lxc-destory يحذف الحاوية، بما في ذلك جذر نظام الملفات؛ راجع صفحات الدليل للأوامر السابقة للمزيد من المعلومات؛ أمثلة: sudo lxc-ls --fancy sudo lxc-start --name u1 --daemon sudo lxc-info --name u1 sudo lxc-stop --name u1 sudo lxc-destroy --name u1 مجالات أسماء المستخدم تسمح الحاويات دون امتيازات للمستخدمين بإنشاء وإدارة الحاويات دون الحصول على امتيازات الجذر؛ أساس هذه الميزة هو ما يسمى «مجالات أسماء المستخدم» (user namespaces)، إن مجالات أسماء المستخدم هيكليةٌ، حيث تكون المهام ذات امتيازات في مجال الأسماء الأب قادرة على ربط معرِّفاتها إلى مجالات أسماء الأبناء؛ افتراضيًا، كل مهمة على المضيف تعمل في مجال أسماء مبدئي (initial user namespace)، حيث المجال الكامل لمعرفاتها مربوطٌ مع المجال الكامل؛ يمكن مشاهدة ذلك بالنظر إلى ‎/proc/self/uid_map و ‎/proc/self ‎/gid_map؛ اللذان سيظهران القيمة «‎‎0 0 4294967295‎» عندما يُقرأ من مجال الأسماء المبدئي؛ وفي أوبنتو 14.04، المستخدمون الجدد الذين يُنشؤون يكون لهم افتراضيًا مجال من معرفات المستخدم؛ هذه القائمة من المعرفات المُسنَدة يمكن أن تُشاهد في الملفين ‎/etc/subuid و ‎/etc/subgid؛ انظر إلى صفحات الدليل الموافقة لهم للمزيد من المعلومات؛ ويبدأ subuid و subgid عرفيًا من المعرف 100000 لتجنب التضارب مع مستخدمي النظام. إذا أُنشِئ المستخدم في إصدارة قديمة، فيمكنك منحه مجالًا من المعرفات باستخدام usermod، كما يلي: sudo usermod -v 100000-200000 -w 100000-200000 user1 برنامجا newuidmap و newgidmap هما برنامجا setuid-root في حزمة uidmap، اللذان يُستخدمان داخليًا بواسطة lxc لربط subuids و subgids من المضيف إلى حاوية دون امتيازات؛ ويتأكدان من أن المستخدم يربط المعرفات المصرَّح بها فقط من ضبط المضيف. الاستخدام الأساسي دون امتيازات لإنشاء حاويات دون امتيازات، فإن هنالك خطوات أولية ضرورية؛ حيث تحتاج إلى إنشاء ملف ضبط حاوية افتراضي، مُحدِّدًا ربط المعرفات الذي تريده وضبط الشبكة، بالإضافة إلى ضبط المضيف للسماح لمستخدم دون امتيازات بالارتباط إلى شبكة المضيف؛ يفترض المثال الآتي أنك ربطت معرفات المستخدم والمجموعة ذات المجال 100000 – 165536. mkdir -p ~/.config/lxc echo "lxc.id_map = u 0 100000 65536" > ~/.config/lxc/default.conf echo "lxc.id_map = g 0 100000 65536" >> ~/.config/lxc/default.conf echo "lxc.network.type = veth" >> ~/.config/lxc/default.conf echo "lxc.network.link = lxcbr0" >> ~/.config/lxc/default.conf echo "$USER veth lxcbr0 2" | sudo tee -a /etc/lxc/lxc-usernet بعد ذلك، يمكنك إنشاء حاويات دون امتيازات بنفس طريقة إنشاء حاويات بامتيازات، لكن ببساطة دون sudo: lxc-create -t download -n u1 -- -d ubuntu -r trusty -a amd64 lxc-start -n u1 -d lxc-attach -n u1 lxc-stop -n u1 lxc-destroy -n u1 التشعب لكي نشغِّل حاويات داخل حاويات – الأمر الذي يُشار إليه بتشعّب الحاويات – فإن سطرين يجب أن يوجدا في ملف ضبط الحاوية الأب: lxc.mount.auto = cgroup lxc.aa_profile = lxc-container-default-with-nesting سيسبب السطر الأول بدمج مقبس مدير مجموعات التحكم في الحاوية، لذلك سيكون lxc داخل الحاوية قادرًا على إدارة مجموعات التحكم للحاويات المتشعبة الخاصة به؛ أما السطر الثاني فيسبب تشغيل الحاوية بوضع أكثر سماحيةً بالنسبة إلى AppArmor، مما يسمح للحاوية بإجراء عمليات الوصل اللازمة لبدء تشغيل الحاويات؛ لاحظ أن سياسة AppArmor التي ستُطبَّق أقل أمنًا من السياسة العادية أو سياسة حاوية دون امتيازات؛ راجع القسم «AppArmor» في هذا الدرس القادم لمزيدٍ من المعلومات. الضبط العام تُستخدَم ملفات الضبط الآتية من LXC؛ للاستخدام ذو الامتيازات، فإنها ستتواجد في مجلد ‎/etc/lxc، بينما للاستخدام دون امتيازات فستكون موجودةً في ‎~/.config/lxc. lxc.conf: يُحدِّد اختياريًا القيم البديلة لمختلف خيارات ضبط lxc، بما فيها lxcpath، والضبط الافتراضي، ومجموعات التحكم التي ستُستخدَم، ونمط إنشاء مجموعة تحكم، وإعدادات الواجهات الخلفية لتخزين lvm و zfs. default.conf: يحدد الضبط الذي يجب أن يحتويه كل ملف ضبط للحاويات المُنشأة حديثًا؛ يحتوي هذا الملف عادةً على الأقل على قسم للشبكة؛ ويحتوي على قسم لربط المعرفات للمستخدمين دون امتيازات. lxc-usernet.conf: يحدد كيف يوصل المستخدمون دون امتيازات حاوياتهم إلى شبكة مملوكة من المضيف. الملفان lxc.conf و default.conf موجودان في ‎/etc/lxc و ‎$HOME/.config/lxc؛ بينما الملف lxc-usernet.conf هو ملف لعموم المضيف. افتراضيًا، تقبع الحاويات في مجلد ‎/var/lib/lxc بالنسبة للمستخدم الجذر، و ‎$HOME/.local/share/lxc عدا ذلك؛ يمكن تحديد المسار لجميع أوامر lxc باستخدام المعامل «‎-P|--lxcpath». ضبط الشبكة افتراضيًا، يُنشِئ LXC مجال أسماء شبكي خاص لكل حاوية، الذي يتضمن مجموعة الاتصال الشبكي من الطبقة الثانية (layer 2)، تتصل الحاويات عادةً إلى العالم الخارجي إما بالحصول على بطاقة شبكية فيزيائية، أو عبر نفق veth يُمرَّر إلى الحاوية؛ ينُشِئ LXC جسر NAT، الذي هو lxcbr0 عند إقلاع المضيف؛ والحاويات المُنشَأة باستخدام ملف الضبط الافتراضي سيكون لها بطاقة شبكية veth تكون نهايتها موصولةٌ إلى الجسر lxcbr0، يمكن للبطاقة الشبكية أن تتواجد في مجال أسماء واحد في وقتٍ واحد، لذلك البطاقة الشبكية الفيزيائية المُمررة إلى الحاوية ستكون غير قابلة للاستخدام في المضيف. من الممكن إنشاء حاويات دون مجال أسماء شبكي خاص، ففي هذه الحالة، ستحصل الحاوية على وصول إلى شبكة المضيف مثل أي تطبيق آخر، لاحظ أنه هذا خطير خصوصًا إذا كانت الحاوية تُشغِّل توزيعة تستخدم upstart، مثل أوبنتو، لأن البرامج التي «تتحدث» إلى init، مثل shutdown، سيتحدثون عبر مقبس مجال يونكس مجرد (abstract Unix domain socket) إلى upstart للمضيف، مما سيوقف تشغيل المضيف! لمنح الحاويات في lxcbr0 عنوان IP ثابت بناءً على اسم المضيف، فيمكنك كتابة هذه المدخلات إلى ‎/etc/lxc/dnsmasq.conf: dhcp-host=lxcmail,10.0.3.100 dhcp-host=ttrss,10.0.3.101 إذا كان من المطلوب أن يُسمَح بالوصول إلى الحاوية من الخارج، فهنالك عدِّة طرق للالتفاف على ذلك، إحداها هي استخدام iptables لتمرير منافذ المضيف إلى الحاوية، فمثلًا: iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 587 -j DNAT \ --to-destination 10.0.3.100:587 طريقة أخرى هي إنشاء جسر إلى إلى البطاقة الشبكية للمضيف (راجع درس ضبط الشبكة لمزيدٍ من المعلومات)؛ ثم حدد جسر المضيف في ملف ضبط الحاوية بدلًا من lxcbr0، فمثلًا: lxc.network.type = veth lxc.network.link = br0 في النهاية، يمكنك سؤال LXC ليستخدم macvlan كبطاقة شبكية للحاوية؛ لاحظ أن لهذه الطريقة حدود واعتمادًا على الضبط قد لا تتمكن الحاوية من «التحدث» إلى المضيف نفسه، وبالتالي الخياران السابقان أفضل ويُستخدمان أكثر. هنالك عدِّة طرق لتحديد عنوان IP للحاوية، فأولًا، يمكنك استخدام lxc-ls --fancy الذي سيطبع عناوين IP لجميع الحاويات التي تعمل؛ أو lxc-info -i -H -n C1 الذي سيطبع عنوان IP للحاوية C1؛ إذا كان dnsmasq مثبتًا على المضيف، فيمكنك إضافة قيد إلى ‎/etc/dnsmasq.conf كما يلي: server=/lxc/10.0.3.1 بعد أن يستبين dnsmasq عنوان C1.lxc محليًا، فيمكنك تنفيذ: ping C1 ssh C1 للمزيد من المعلومات، راجع صفحة دليل lxc.conf ومثال ضبط الشبكة في ‎/usr/share/doc/lxc ‎/examples/‎. ترجمة -وبتصرف- للمقال Ubuntu Server Guide: LXC.
  25. سنكمل الآن ما تعلمناه في الدرس السابق عن السلاسل النصية. وقد كان محتوى الدرس الأول كالآتي: إنشاء والوصول إلى السلاسل النصية باستخدام $ و {}، واستخدام الدالة strlen()‎. البحث في السلاسل النصية باستعمال الدوال: strstr()‎ strpos()‎ و strrpos()‎ substr_count()‎ strpbrk()‎ الاستبدال في السلاسل النصية باستخدام الدوال: str_replace()‎ ()substr_replace ()strtr strtr()‎ التعامل مع حالات الحروف الكبيرة (uppercase) والصغيرة (lowercase) باستخدام الدوال: strtolower()‎ strtoupper()‎ ucfirst()‎ lcfirst()‎ ucword()‎ النسخ غير الحساسة لحالة الأحرف لجميع الدوال السابقة. أما هذا الدرس فسيحتوي على ما يلي: تنسيق السلاسل النصية باستخدام printf()‎، بما في ذلك تعيين دقة الأرقام العشرية. إزالة الفراغات (trimming) من أطراف السلاسل النصية باستعمال الدوال: trim()‎ ltrim()‎ rtrim()‎ إضافة حواشي (paddings) للنصوص باستعمال الدوال: str_pad()‎ STR_PAD_RIGHT STR_PAD_LEFT STR_PAD_BOTH التفاف (wrapping) النصوص عبر استعمال الدالة wordwrap()‎. تنسيق الأرقام باستخدام الدالة number_format()‎. الوقت والتاريخ. تنسيق السلاسل النصية الدالة printf()‎ الدالة printf()‎ هي أداةٌ قويةٌ تمكِّنك من تنسيق السلاسل النصية بطرقٍ مختلفة، تأخذ printf()‎ وسيطًا يُسمى سلسلة التنسيق (format string) يُتبَع عادةً بوسيطٍ اختياري أو أكثر يحتوي على السلسلة أو السلاسل التي ستوضع في السلسلة السابقة، ثم ستطبع هذه الدالة النتيجة النهائية. سلسلة التنسيق تحتوي على نصٍ عادي وفيه مُحدِّدات التنسيق (conversion specifications)، يتطلب وجود كل محدد تنسيق وسيطًا إضافيًا يُمرَّر إلى printf()‎، ومهمته هي تنسيق ذاك الوسيط ووضعه في سلسلة التنسيق، ثم بعد ذلك ستُعرَض السلسلة النهائية المُنسَّقة؛ تبدأ محددات التنسيق دومًا بإشارة النسبة المئوية (%)، ربما يبدو الأمر معقدًا للوهلة الأولى، لكن مثالًا بسيطًا كهذا سيجعل إيصال الفكرة أمرًا هينًا: <?php // الناتج: "Pi rounded to a whole number is: 3" printf( "Pi rounded to a whole number is: %d", M_PI ); ?> إن عبارة "Pi rounded to a whole number is: %d" هي سلسلة التنسيق، و "%d" ضمن السلسلة السابقة هي محدد للتنسيق، وفي هذه الحالة، يُخبِر هذه المحدد الدالة printf()‎ أن تقرأ الوسيط الإضافي وتضعه -بعد تنسيقه كعدد صحيح- في سلسلة التنسيق؛ الوسيط الإضافي هو الثابت M_PI الموجود في لغة PHP، التي يُمثِّل القيمة التقريبية للعدد π بعدد معيّن من المنازل العشرية (14 منزلة افتراضيًا)؛ أي أنَّ النتيجة النهائية لاستدعاء الدالة السابقة هي طباعة سلسلة التنسيق مع استبدال "%d" بقيمة الثابت π مُقرَّبًا إلى أقرب عدد صحيح. المزيد من المُحدِّدات: b: معاملة الوسيط كعدد صحيح (integer) وتنسيقه كعدد ثنائي. c: معاملة الوسيط كعدد صحيح وتنسيقه كمحرف بنظام ASCII الذي يحمل نفس القيمة العددية. d: معاملة الوسيط كعدد صحيح وتنسيقه كعدد عشري مع إشارة (signed). e: تنسيق الوسيط بالطريقة العلمية (مثلًا، 3.45e+2). f: تنسيق الوسيط كعدد ذو فاصلة عائمة (floating point)، آخذًا بالحسبان إعدادات «المحليّة» (locale) الحالية (على سبيل المثال، تستعمل بعض الدول الأوروبية الفاصلة كعلامة تفصل الأرقام الصحيحة عن الأرقام العشرية عوضًا عن النقطة؛ أي 3,14 بدلًا من 3.14). F: تنسيق الوسيط كعدد ذو فاصلة عائمة بغض النظر عن إعدادات المحليّة. o: معاملة الوسيط كعدد صحيح وتنسيقه كعدد بالنظامي الثماني. s: تنسيق الوسيط كسلسلة نصيّة. u: معاملة الوسيط كعدد صحيح وتنسيقه كعدد عشري دون إشارة (unsigned). x: معاملة الوسيط كعدد صحيح وتنسيقه كعدد بالنظام الست عشري بأحرفٍ ذات الحالة الصغيرة (lowercase). X: معاملة الوسيط كعدد صحيح وتنسيقه كعدد بالنظام الست عشري بأحرفٍ ذات الحالة الكبيرة (uppercase). %: إظهار إشارة النسبة المئوية (%)، لا يتطلب هذا المُحدِّد وجود وسيط إضافي. إضافة حواشي إلى المخرجات يمكنك إضافة محارف إلى يسار (افتراضيًا) أو إلى يمين الوسيط الذي يتم تنسيقه كي يُصبِح بعرضٍ معيّن، وهذا مفيدٌ إن أردت أن تُضيف أصفارًا تسبِق الأعداد، أو أن تحاذي عدِّة سلاسل نصية أفقيًا عبر إضافة فراغات على جوانبها. <?php // الناتج‏‎: "000123" printf( "%06d<br/>", 123 ); // الناتج‏‎: "004567" printf( "%06d<br/>", 4567 ); // الناتج‏‎: "123456" printf( "%06d<br/>", 123456 ); ?> دقة الأرقام عندما تُظهِر الأعداد ذات الفاصلة العائمة باستخدام المحدِّد f أو F، يمكنك استعمال مُحدِّد لدقة الأرقام لتعيين عدد المنازل العشرية التي سيُقرَّب (أو يدوَّر) إليها الرقم. لإضافة مُحدِّد دقة الأرقام، أضف نقطة (.) يلحقها عدد المنازل العشرية التي ستستخدم، قبل مُحدِّد النوع؛ انظر إلى المثال الآتي للتوضيح: <?php // الناتج‏‎: "123.456700" (الدقة الافتراضية) printf( "%f<br />", 123.4567 ); //الناتج ‏‎: "123.46" printf("%.2f<br />", 123.4567 ); // الناتج‏‎: "123" printf("%.0f<br />", 123.4567 ); // الناتج‏‎: "123.4567000000" printf("%.10f<br />", 123.4567 ); ?> يمكنك استخدام مُحدِّد الحاشية مع مُحدِّد الدقة، في حال أردت أن يكون طول الرقم معينًا (بما في ذلك الأرقام بعد الفاصلة العشرية، والفاصلة نفسها أيضًا): <?php // الناتج‏‎: "123.46" printf( "%.2f<br />", 123.4567 ); // الناتج‏‎: "000000123.46" printf( "%012.2f<br />", 123.4567 ); // الناتج‏‎: " 123.4567" printf( "%12.4f<br />", 123.4567 ); ?> إزالة الفراغات من أطراف السلاسل النصية قد لا ترغب في بعض الأحيان بوجود أيّة فراغات قبل أو بعد السلسلة النصية، وفي هذه الحالة نستعمل هذه الدوال: trim()‎: إزالة الفراغات (white-space) من بداية ونهاية السلسلة النصية. ltrim()‎: إزالة الفراغات من بداية السلسلة النصية فقط. rtrim()‎: إزالة الفراغات من نهاية السلسلة النصية فقط. انظر إلى ناتج المثال الآتي للتوضيح: <?php echo trim(" hsoub "); echo ltrim(" hsoub "); echo rtrim(" hsoub "); ?> إضافة حواشي إلى النصوص لقد رأينا سابقًا كيف يمكننا استخدام الدالة printf()‎ لإضافة حواشي إلى بداية أو نهاية سلسلة نصيّة؛ لكن لغة PHP توفِّر دالةً مخصصةً لهذا الأمر اسمها str_pad()‎، وهي أكثر مرونةً من printf()‎ وأسهل استخدامًا. لاستعمال الدالة str_pad()‎، مرِّر السلسلة التي ستتم إضافة الحواشي إليها، وعرض السلسلة النهائي، وستعيد الدالة السلسلة وقد تمت إضافة فراغات إلى يمينها (افتراضيًا): <?php echo '<pre>'; echo str_pad( "Hello, world!", 20 ); // الناتج‏‎: "Hello, world! " echo '</pre>'; ?> ولإضافة محارف أخرى غير الفراغ كحاشية، فمرِّر سلسلةً نصيةً كوسيطٍ ثالثٍ اختياري، لاحظ أنَّك تستطيع استخدام محرف وحيد أو أكثر من محرف؛ وفي الحالة الأخيرة، ستُكرَّر السلسلة النصية حسب العرض المُحدَّد: <?php // الناتج‏‎: "Hello, world!*******" echo str_pad( "Hello, world!", 20, "*" ) . "\n"; // الناتج‏‎: "Hello, world!1231231" echo str_pad( "Hello, world!", 20, "123" ) . "\n"; ?> يمكنك جعل الدالة str_pad()‎ تضع الحاشية على يسار السلسلة النصية، أو على يمنيها ويسارها معًا؛ وذلك بتمرير وسيط ثالث اختياري يحتوي على ثوابت الدالة المذكورين في الجدول الآتي: STR_PAD_RIGHT: إضافة الحاشية على اليمين (الحالة الافتراضية)، مما يحاذي السلسلة النصيّة إلى اليسار. STR_PAD_LEFT: إضافة الحاشية على اليسار، مما يحاذي السلسلة النصيّة إلى اليمين. STR_PAD_BOTH: إضافة الحاشية على الطرفين الأيمن والأيسر معًا، مما يؤدي إلى توسيط السلسلة النصية ما أمكن. مثال: (لاحظ وجود 3 محارف للحاشية على اليسار، و4 على اليمين؛ إذ لا يمكن توسيطها تمامًا): <?php // الناتج‏‎: "***Hello, world!****" echo str_pad( "Hello, world!", 20, "*", STR_PAD_BOTH ) . "\n"; ?> التفاف النصوص أحيانًا يكون عندك نصٌ طويلٌ مثل رسائل البريد الإلكتروني أو المقالات، وربما تريد أن تُقسَّم الأسطر بعرضٍ مختلف. تفعل الدالة wordwrap()‎ هذا الأمر؛ يمكنك تحديد عدد المحارف في السطر عبر هذه الدالة، انظر إلى هذا المثال أولًا: <?php $myString = "this is my string which is very long. it is containing five lines and a number of white spaces"; echo wordwrap ($myString); echo "<br><br><br>"; echo wordwrap ($myString, 40); echo "<br><br><br>"; echo wordwrap ($myString, 40, "<br />"); ?> لاحظ النتيجة بنفسك بفتح مصدر صفحة الويب ورؤية النتيجة لمختلف الجمل بقيمٍ مختلفة للوسيط الثاني (غير 40، مثل 10، أو 15، أو 20 ...إلخ.). تنسيق الأعداد تُستعمَل الدالة number_format()‎ لتنسيق الأعداد، وتستقبل وسيطًا أو وسيطين أو أربعة وسائط. أول وسيط هو العدد الذي سيُنسَّق الوسيط الثاني هو عدد المنازل العشرية الوسيط الثالث هو المحرف الذي سيفصل بين الأعداد الصحيحة والأرقام العشرية أما رابع وسيط، فيُحدِّد المحرف الذي سيفصل بين الآلاف في الأعداد الصحيحة يوضَّح ما سبق بالمثال الآتي: <?php $myNumber = 12344555.3453; // الناتج‏:‎ 1,23,44,555.3453 echo number_format($myNumber); echo "<br>"; // الناتج‏:‎ 1,23,44,555.34 echo number_format($myNumber,2); echo "<br>"; // الناتج‏:‎ 1 23 44 555,3453 echo number_format($myNumber,2,',',' '); echo "<br>"; ?> الوقت والتاريخ يلزم الحصول على الوقت والتاريخ وعرضه بطريقة مناسبة كثيرًا في تطوير الويب، ولهذا توفِّر PHP الأدوات اللازمة لذلك. أهم تلك الأدوات هي الدالة date()‎ التي تقبل سلسلةً نصيةً تُحدِّد تنسيق المخرجات، وتتألف من محارف التنسيق الآتية: d: يعيد رقم اليوم من الشهر، وتتراوح قيمته من 01 إلى 31. استخدام المحرف j للحصول على رقم اليوم دون 0 بادئة. m: يعيد رقم الشهر، وتتراوح قيمته من 01 إلى 12. استخدام المحرف n للحصول على رقم الشهر دون 0 بادئة. w: الحصول على رقم اليوم من الأسبوع، وتتراوح قيمته بين 0 الذي يشير إلى يوم الأحد، و6 ليوم السبت. h: الحصول على الساعة الحالية بنظام 12 ساعة. استخدم g للحصول على الساعة بنظام 24 دون 0 بادئة. استخدام a للحصول على am أو pm، و A للحصول على AM أو PM. H: الحصول على الساعة الحالية لكن بنظام 24 ساعة. استخدم G للحصول على الساعة بنظام 24 دون 0 بادئة. i: الحصول على الدقائق. s: الحصول على الثواني. Y: الحصول على السنة على هيئة أربعة أرقام. ويمكن تمرير وسيطٍ اختياري هو بصمة الوقت (timestamp) لكي تُعيد الدالة date()‎ قيمة التاريخ والوقت لها، لكن إن لم تُحدِّد بصمة الوقت، فستؤخذ القيمة من ناتج الدالة time()‎ التي تُعيد بصمة الوقت مقاسةً بالثواني منذ بداية وقت يونكس (Unix Time) (أي منتصف ليلة رأس السنة عام 1970). <?php // الناتج‎: 12:31:55 echo date('H:i:s'); // الناتج‎: 2015/12/23 echo date('Y/m/d'); ?> ملاحظة: يجدر بالذكر أنَّ الدالة mktime()‎ تنُشِئ بصمة الوقت لتاريخ معيّن كي تستعملها مع الدالة date، شكلها العام هو الآتي: mktime($hour, $minute, $second, $month, $day, $year); حيث أول وسيط هو الساعة، والثاني هو الدقيقة، والثالث هو الثانية، والرابع هو الشهر، والخامس هو اليوم من الشهر، والسادس هو السنة؛ مثالٌ عن استعمالها: <?php // الناتج هو مثلًا‏‎: 1450872238 $current_time = time(); echo $current_time; // ناتج بصمة الوقت هو‎: 1030134920 $past_time = mktime(22, 35, 20, 8, 23, 2002); echo $past_time; // الناتج هو‎: 12-23-2015 13:03 // يمكن أن نحذف الوسيط الثاني، لأن الدالة date ستعتمد الوقت الحالي المُعادة من الدالة time افتراضيًا echo 'current date is: ' . date ('m-d-Y H:i' , $current_time); // الناتج هو‎: 08-23-2002 22:35 // يمكن أن نحذف الوسيط الثاني، لأن الدالة date ستعتمد الوقت الحالي المُعادة من الدالة time افتراضيًا echo 'a date in the past: ' . date ('m-d-Y H:i' , $past_time); ?> تمرين اكتب برنامجًا يأخذ تاريخ ميلاد المستخدم ثم يُظهِر عمره بالسنوات والأشهر والأيام؛ مثلًا: 35 سنة، و 4 أشهر، و23 يومًا. المصادر مقال Strings in PHP part-2 لصاحبه Harish Kumar. صفحة sprintf، و date في دليل PHP وغيرها.
×
×
  • أضف...