لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 06/07/15 في كل الموقع
-
مقدمة من أهم أسباب انتشار ووردبريس وسيطرته على سوق أنظمة إدارة المحتوى هو الكم الهائل من إضافاته المجانية والمدفوعة والتي تقدم العديد من الخصائص والمميزات والتحسينات الإضافية التي لا تتوفر بصورة افتراضية في قلب ووردبريس WordPress Core. في هذا المقال سنتعرف سوّيًا على أساسيات إنشاء إضافة لووردبريس لكن قبل الدخول في لب الموضوع دعنا نجب على سؤال مُهمّ وكذلك تعريف إضافات ووردبريس. لماذا ننشئ إضافات ووردبريس هنالك قاعدة مهمة في تطوير ووردبريس، وهي: إياك أن تلمس الشيفرات Codes الأساسية لووردبريس أي لا تُعدّل على قلب ووردبريس WordPress Core وذلك لأنه عند التحديث سيتمّ استبدال بعض أو كل ملفات ووردبريس الأساسية، وبالتّالي ستفقد التعديلات التي قمت بها، ولهذا السبب نلجأ إلى تطوير إضافات عند الحاجة لإضافة التحسينات والخصائص الجديدة على النّظام. تعريف إضافات ووردبريس إضافة ووردبريس عبارة عن دالة أو مجموعة دوال (حزمة شيفرات code package) مكتوبة بلغة php تقوم بإضافة بعض الخصائص لنظام إدارة المُحتوى. وتندمج شيفرات الإضافة معه من خلال واجهة برمجية API يوفرها له. بالإضافة لملفات php يمكن أن تحتوي الإضافة على ملفات أخرى مثل ملفات JavaScript, CSS وبعض الصور. إنشاء إضافة ووردبريس هذه بعض الخطوات المهمة عند إنشاء أي إضافة ووردبريس وبعض الأمور التي يجب مراعاتها. اختيار اسم الإضافة في البدء عليك أن تقوم باختيار اسم فريد للإضافة التي تود إنشائها بحيث لا يكون الاسم مستخدما من قبل من إحدى إضافات ووردبريس حتى لا يحدث تعارض بين الإضافات مستقبلا. إنشاء مجلد الإضافة بعد اختيار اسم الإضافة تقوم بإنشاء مجلد لها بالاسم الذي اخترناه ويكون هذا المجلد داخل مجلد plugins الخاص بالإضافات والموجود داخل مجلد wp-content كما موضح بالصّورة. يمكنك إنشاء ملف php بنفس الاسم بدلًا عن إنشاء مُجلّد كامل لكن يُفضّل أن تنشئ مُجلّدًا خاصًّا حتى يستوعب بقية ملفات الإضافة، ما لم تكن الإضافة تحتوي على ملف php واحد فقط. إنشاء الملف الأساسي للإضافة بعد إنشائك للمُجلّد في الخطوة أعلاه عليك أن تقوم بإنشاء ملف php له نفس اسم المجلد السابق، وهذا الملف هو الملف الأساسي للإضافة والذي سيستخدمه ووردبريس للتّعرف على بعض الأمور المُتعلّقة بالإضافة مثل اسمها واسم المُبرمج ورخصة الإضافة وغيرها، وهذا يتم من خلال استخدام ترويسة للملف الأساسي للإضافة وهذه هي الصّيغة العامة لها: <?php /** * Plugin Name: هنا تكتب اسم الإضافة والذي يجب أن يكون فريدًا * Plugin URI: هنا تكتب رابط موقع الإضافة * Description: هنا نبذة مختصرة عن الإضافة * Version: رقم نسخة الإضافة، مثلا 1.0.0 * Author: اسم مطور الإضافة * Author URI: رابط موقع مطور الإضافة * License: اسم رخصة الإضافة */ السّطر الإجباري هو سطر اسم الإضافة فقط، ويتمّ استخدام بقية السطور (في حال ورودها) في إنشاء معلومات الإضافة التي تظهر في لوحة تحكم الإضافات عند استعراض الإضافة. برمجة الإضافة قبل أن تبدأ في برمجة الإضافة تحتاج لمعرفة بعض الأمور العامة في برمجة ووردبريس بالإضافة لبعض التسهيلات التي يقدمها ووردبريس للمبرمجين مثل الواجهات البرمجية الجاهزة WordPress API's. الخطافات في ووردبريس WordPress Hooks ( الدخول الى عمق ووردبريس) من أهم المفاهيم في ووردبريس مفهوم الخطافات hooks والتي تنقسم إلى قسمين: الأحداث actions والمُرشّحات filters. 1- الإجراءات في ووردبريس WordPress Actions فلنفترض أنه لدينا دالة معينة نريد أن يتم تنفيذها في لحظة معينة أثناء دورة حياة ووردبريس أو عند حدوث حدث معين، كيف يمكننا إنجاز هذا الأمر؟ أحد الخيارات أن نقوم بمناداة هذه الدالة بالطريقة العادية في المكان الذي نريدها أن تُنفّذ فيه، لكن هذا الأمر يتطلّب التّعديل على ملفات ووردبريس الأساسية. لك القاعدة التي ذكرناها مسبقا: إياك أن تلمس الشيفرات الأساسية لووردبريس تمنعنا من القيام بذلك. الحل الآخر هو أن نستخدم الإجراءات actions وهي عبارة عن آليّة رائعة يوفرها ووردبريس تمكّنك من إضافة الدّوال الخاصّة بك لتُنفّذ في وقت معين أثناء تنفيذ الشيفرات الخاصة بووردبريس إذًا، يمكننا أن نعرف الإجراءات بأنها طريقة تمكننا من إخبار ووردبريس بتنفيذ دالة معينة عند حصول حدث معين وبهذه الطريقة نستطيع الدخول إلى عمق ووردبريس من دون التعديل المباشر على ملفاته الأساسية Core files. يتم هذا كله من خلال ربط الدّوال التي تريدها مع الأحداث الجاهزة التي يوفرها ووردبريس والتي يقوم بتنفيذها أثناء دورة حياته وهذا ما يجعلك قادرا على تنفيذ الدّوال الخاصة بك في أي مكان تريده تقريبا. إذا كنّا نريد مثلا تنفيذ الدّالة my_function عندما ينفذ ووردبريس الحدث my_action علينا ببساطة أن نخبره بذلك من خلال إضافة هذه الدالة إلى هذا الحدث. ويتم ذلك من خلال الدالة add_actions بالطريقة التالية add_action('my_action', 'my_function'); 1-1مثال للإجراءات في ووردبريس فلنأخذ هذا المثال الذي يقوم بإرسال بريد إلى مدير الموقع (الدالة المراد تنفيذها) كلما نشر مقالًا جديدًا (الحدث الذي تنفذ عنده الدالة) add_action('publish_post', 'email_admin'); function email_admin(){ // هنا يتم إرسال البريد الإلكتروني إلى مدير الموقع } 2-المرشحات في ووردبريس WordPress Filters المُرشّحات هي النوع الثاني من الخطّافات التي يوفرها ووردبريس وهي مختصّة أكثر بالبيانات حيث أنها تتيح لنا إمكانية التعديل على قيم المتغيرات (اسم المقال مثلا) قبل حفظها في قاعدة البيانات أو قبل عرضها للمستخدم، وكما هو ظاهر من اسمها فهي تتيح لنا إمكانية ترشيح البيانات، بمعنى إدخالها إلى filter مُعيّن يُعدّل عليها قبل إخراجها من الجانب الآخر من هذا المُرشّح . بطبيعة الأمر وكما هو الحال بالنّسبة للأحداث فإن ووردبريس يُوفّر لك إمكانية إدخال أغلب مُتغيّراته في المُرشّحات التي تريدها من خلال اسم المتغير، ويمكنك الاطّلاع على المُتغيّرات التي يمكنك تمريرها عبر المُرشّحات بزيارة التوثيق الرسمي للمرشحات. يتم إضافة فلتر معين my_filter لبيانات معينة my_data من خلال الدّالة add_filter بالطريقة التالية add_filter( 'my_data', 'my_filter' ); حيث أن my_filter هي الدّالة التي ستقوم بالتعديل على البيانات ويتم تمرير البيانات لها من قبل ووردبريس أي أنها يجب أن تستقبل my_data كمعامل parameter. 2-1مثال للمرشحات في ووردبريس هذا المثال يقوم بتعديل محتوى المقال the_content حيث يقوم بإضافة التّوقيع لمُحتويات كل مقال في الموقع add_filter ( 'the_content', 'add_signiture' ); function add_signiture($the_content){ // هنا نقوم بإضافة الشيفرات التي تضيف التّوقيع لمحتويات المقال return $the_content; // بعد ذلك تقوم الدّالة بإرجاع القيم الجديدة لمحتويات المقال } هذه نظرة سريعة فقط على الخطافات في ووردبريس ولكن ما زال هنالك العديد من الأشياء المتعلقة بهذه الآلية الرّائعة التي يوفرها ووردبريس منها: إمكانية إضافة الـhooks الخاصة بك. تعديل أولوية تنفيذ الدّوال التابعة لنفس الـhook. ويمكنك التعرف على المزيد حول موضوع الخطافات في التّوثيق الرسمي لPlugin API وسوم القالب Template Tags قد تبدو الترجمة الحرفية مُضلّلة نوعًا ما خصوصا إذا علمت أن المقصود من وسوم القالب هو مجموعة من الدّوال الجاهزة التي يوفرها ووردبريس والتي تختصر لك الكثير من العمل، لذلك من الجيد أن تلقي نظرة عليها قبل البدء في برمجة الإضافة الخاصة بك. الجدير بالذّكر أن يتم استخدام كثير من هذه الدّوال داخل الحلقة الخاصّة بجلب المقالات في ووردبريس WordPress Loop والتي تستخدم بكثرة في القوالب، ولعل هذا هو سرّ تسميتها بوسوم القالب. حفظ بيانات/ الإضافة في قاعدة البيانات الكثير من إضافات ووردبريس إن لم يكن معظمها تحتاج لاستقبال بعض البيانات من مدير الموقع (خيارات الإضافة مثلا) وحفظها في قاعدة البيانات لاسترجاعها واستخدامها لاحقا، ولهذا السبب يتيح لنا ووردبريس عدّة طرق لحفظ البيانات في قاعدة بيانات الموقع واختيار الطريقة المناسبة يعتمد على نوع البيانات المراد حفظها. هذه الطرق هي: 1- استخدام آلية "الخيارات options" التي يوفرها ووردبريس وهذه الطريقة مناسبة في حالة كون البيانات المراد حفظها صغيرة نسبيا ولا تتغير كثيرا مثل البيانات التي تتوقع من مدير الموقع إدخالها بعد تنصيب الإضافة مباشرة (خيارات الإضافة) والتي نادرا ما تتغير. في هذه الطريقة يتم حفظ بيانات الإضافة في جدول wp_options والذي يستخدمه ووردبريس لحفظ خياراته الخاصة. 2-استخدام البيانات الوصفية الخاصة بالمقالات Post Meta وهذه الطريقة مناسبة في حالة البيانات المتعلقة بمقال أو صفحة مفردة (مثلا مزاج الكاتب عند كتابة المقال) 3-استخدام الفئات المخصّصة وهذه مناسبة لتصنيف البيانات مثل تصنيف المقالات أو المستخدمين أو التعليقات. 4- إنشاء جدول جديد في قاعدة البيانات هذه الطريقة مناسبة مع البيانات التي لا يصلح معها أي نوع من الأنواع الثلاثة السابقة والتي تتوقع أن تتزايد مع الزمن والتي لا يمكن حصرها في اسم محدد. الاستفادة من الواجهات البرمجية API's التي يوفرها ووردبريس يحتوي ووردبريس على مجموعة ضخمة من الدّوال والأصناف Classes المفيدة التي تمثل بوّابات تربطك بقلب ووردبريس WordPress Core بالإضافة إلى توفيرها لدوال مفيدة في كثيرا من الأمور البرمجية العامة في مجال الويب، وهذه أمثلة لبعض الواجهات البرمجية التي يوفرها ووربريس Plugin API: تتيح لك الدّوال المستخدمة في الاستفادة من ميكانيكية الخطافات وكذلك الدّوال المساعدة لإنشاء الخطافات الخاصة بك. Database API : واجهة برمجية تسهل التعامل مع قاعدة بيانات ووردبريس. Rewrite API: تتيح لك إمكانية تغير صيغة روابط المقالات والموقع عموما والتحكم فيها. Filesystem API: للتعامل مع نظام الملفات في نظام التشغيل. وغيرها الكثير من الواجهات البرمجية ويمكنك الإطلاع عليها من هنا أمور عامة عليك مراعاتها عند برمجة الإضافة 1- الاهتمام بأمن الشيفرة Code Security كما هو الحال عند برمجة أي سكربت من خلال لغة php عليك مراعاة أن يكون خاليا من الثغرات التي قد تعرض الموقع لخطر الاختراق، نفس الأمر ينطبق على إضافات ووردبريس فعليك الاهتمام بإغلاق الثّغرات المعروفة وحماية المدخلات وكذلك ملفات الإضافة بمنع الوصول المباشر لها عن طريق استخدام الشيفرة التالية مثلا defined( 'ABSPATH' ) or die( 'ممنوع الوصول المباشر' ); هنالك أشياء أخرى يجب مراعاتها خصوصا إذا أردت نشر إضافتك في مستودع إضافات ووردبريس، بعض هذه الأمور إجباري وتحتاج لعمله حتى يتم قبول إضافتك ونشرها في المستودع، من هذه الأمور: 2- ملف اقرأني Readme File هو ملف تقوم بتضمينه في مجلد الإضافة باسم readme.txt ويستخدمه مستودع الإضافات لأخذ بعض المعلومات بالإضافة للمعلومات التي تظهر في صفحة الإضافة في المستودع، يمكنك استخدام هذا المُوّلد لإنشائه 3- جعل الإضافة قابلة للترجمة من الأمور الأخرى التي عليك مراعاتها هو جعل الإضافة قابلة للترجمة لعدة لغات localized حيث يمكنك استخدام مكتبة gettext التي يوفرها ووردبريس والتي تجعل من عملية جعل الكلمات قابلة للترجمة أمر سهل، وبعد ذلك يتم إرفاق ملفات اللغة مع الإضافة، ويمكنك قراءة المزيد عن هذا الأمر من هنا 4- استخدام معايير تطوير وودبريس معايير التّطويرCoding Standard هي بعض القواعد العامة التي عليك مراعاتها عند كتابة الشيفرة لتجنب أخطاء التشفير Coding وتسهيل عملية كتابة الشيفرات ومراجعتها وقراءتها خصوصا في حالة تعاون أكثر من شخص في برمجة شيء ما. لووردبريس قواعده الخاصة أيضا والتي يحاول من خلالها المحافظة على قواعد ثابتة في شيفرات ووردبريس وكذلك شيفرات الإضافات والقوالب لذلك يفضل أن تلتزم بهذه القواعد عند كتابة شيفرات الإضافة الخاصة بك. يمكنك قراءة المزيد عن هذه القواعد من هنا. 5- تحديث الإضافة يستخدم مستودع إضافات ووردبريس نظام Subversion لتحديث ولمعرفة آلية القيام بذلك، زر هذه الصّفحة كانت هذه كانت مقدمة سريعة ونبذة مختصرة حاولنا أن نجعلها كمدخل لبرمجة إضافات ووردبريس وما زال هنالك المزيد لمعرفته عن برمجة إضافات ووردبريس في مقالات قادمة.1 نقطة
-
يزداد نمو المواقع يومًا بعد يوم، ومنه إلى ازديار في نسبة مرور البيانات (traffic)، الأمر الّذي من شأنه أن يزيد من الضغط على قواعد البيانات، وباعتبار أنّ قواعد البيانات هي عنق الزجاجة لأداء أي موقع، فعليه يجب أنّ يتمّ إعدادها بشكل مُناسب لِتتولّى الضغط العالي بشكل يُلائم مُتطلّبات العمل.يُمكن اعتبار تقنيّة memcached أحد الطّرق العمليّة في رفع معدّل تخبئة الكائنات في ذاكرة النظام، فهي نظام يعمل على تخزين البيانات وبشكلٍ مؤقّت في الذّاكرة على أن يتمّ استدعائها من الذّاكرة بدلًا من قاعدة البيانات، بحيث تكون هذه البيانات جاهزةً إلى الطلب التّالي ومن الذّاكرة، الأمر الّذي يزيد من سرعة الاستجابة وبشكلٍ ملحوظ. سيتناول هذا الشرح كيفيّة تنصيب وإعداد الذّاكرة المُخبّئة على الخادوم Ubuntu 14.04، ومناقشة ما يلزم من تفاصيل. مُتطلّبات العملقبل بدء العمل، يجب توفّر مُستخدِم ذو صلاحيات وصول عالية، لتطبيق مُحتويات هذه الدليل. تنصيب Memcached وباقي المُقَوِّمات المطلوبةيُمكن تنصيب كافة المُقَوِّمات (components) اللازمة لبدأ العمل من مستودعات أبونتو حيثُ أنّه يوفّرها جميعها. سيتمّ أولًا تحديث الحزم المحلّيّة، ومن ثمّ تنصيب باقي البرامج سيتمّ تنصيب memcached، بالإضافة إلى قواعد البيانات MySQL، ولغة PHP لتتولّى عمليّة الوساطة مع قواعد البيانات، وكما سيتمّ الحاجة إلى تنصيب تَوْسِعَة/إضافة (extension) للغة PHP لِتتولّى عمليّة الربط مع الذّاكرة المُخبّئة (memcached): sudo apt-get update sudo apt-get install mysql-server php5-mysql php5 php5-memcached memcachedيجب الانتباه هنا إلى توفّر توسعتان من الذّاكرة المخبّئة الخاصّة بلغة PHP، تحمل الأولى الاسم php5-memcache، وتحميل الثّانية الاسم php5-memcached، -حرف “d” الإضافي في الثّانية-سيتمّ الاعتماد على الإضافة الثّانية في هذا الشرح بسبب استقرارها، وتقديمها إلى مساحة أكبر من الخيارات والمزايا. إنّ لم تكن قواعد البيانات منصّبة من قبل، فإن المُنصب سيَطلب اختيار وتأكيد كلمة مرور حساب المُدير. ويكون بهذا قد تمّ تنصيب ما سيتمّ الحاجة له خلال الشرح. اختبار تنصيب الذّاكرة المُخبّئةقد يبدو الأمر صعب التصديق، ولكن بهذا الإعداد البسيط فإن الذّاكرة المُخبئة أصبحت جاهزة للعمل، ويُمكن اختبارها بطرقٍ عدّة. تُعتبر الطريقة الأولى سهلة إلى حدٍّ ما، حيث من المُمكن فقط سؤال PHP إن كانت تعلم حول تَوْسِعَة memcached، وفيما إذا كانت مُفعّلة أو لا، وذلك عبر إنشاء صفحة PHP info المعروفة. سيتمّ إنشاء ملف بالاسم info.php في المسار الجذر لخادوم الوب، والّذي سيكون /var/www/html على اعتبار أنّ خادوم الوب هو Apache: sudo nano /var/www/html/info.phpسيتمّ كتابة السطور التّالية في هذا الملفّ، والّتي من شأنها استدعاء دالّة (PHP function)، والّتي من شأنها أنّ تجمع وتطبع معلومات حول الخادوم بصفحة ويب مُنسقة بشكل مقروء ومُبسّط. <?php phpinfo(); ?>يُمكن الآن زيارة خادوم الوب متبوعًا بـ /info.php، وبعد التدرّج في الصفحة إلى القسم الخاص بـ “memcached”، سيكون هناك معلومات مُشابهة إلى التّالي: http://server_domain_name_or_IP/info.php تشير المعلومات السابقة إلى أنّ تَوْسِعَة memcached مُفعّلة، وقد تمّ ربطها مع خادوم الوب. يُمكن أيضًا اختبار فيما إذا كانت خدمة memcached تعمل عن طريقة ترشيح العمليات الحاليّة باستخدام الأداة grep: ps aux | grep memcachedيعرض الأمر السابق معلومات على الشكل التّالي: memcache 6584 0.0 0.0 327448 3004 ? Sl 14:07 0:00 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1 demouser 6636 0.0 0.0 11744 904 pts/0 S+ 14:29 0:00 grep --color=auto memcachedوكما هو الأمر دائمًا مع خدمات أنظمة لينكس فيمكن إيقاف، تشغيل، أو إعادة تشغيل أي خدمة، فالإعادة تشغيل خدمة memcached يُمكن تنفيذ الأمر التّالي: sudo service memcached restartاختبار الذّاكرة المُخبّئةبعد أن تمّ التأكّد من عمل الذّاكرة المُخبّئة، وأن تَوْسِعَة PHP مُتصلة بها ومُفعّلة، فمن المُمكن تجربة استخدامها في تخبئة/تخزين بعض البيانات. سيتمّ كتابة شيفرة/سكريبت PHP آخر لذلك، وسيكون أكثر تعقيدًا من سابقه: sudo nano /var/www/html/cache_test.phpسيتمّ تغليف الشيفرة بوسم PHP: <?php ?>سيتمّ إنشاء حالة/نموذج (instance) من كائن Memcached، وتخزينه في مُتغيّر، ومن ثُمّ سيتمّ تعريف المَوضع الّذي يُمكّن هذا الكائن من الاتصال بخدمة memcache، والّتي تعمل على المنقذ 11211 بالإعداد الافتراضيّ. <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); ?>سيتمّ في الخطوة التّالية إخبار حالة Memcached، المُنشئة في الخطوة السابقة، بالاستعلام (query) عن مفتاح (key) من *التخبئة (cache)، يُمكن أنّ يكون اسم المُفتاح اعتباطيًا باعتبار أنّه لم ينشئ بعد، وليكن “test”، وستُخزّن نتيجة هذا الطلب في المتغيّر $result: <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); $result = $mem->get("test"); ?>سيتمّ في الخطوة التّالية اختبار فيما إذا تمّ الحصول على أي عائد: فلو وجدت memcached مفتاحًا بالاسم “test”، سيتمّ طباعته مع قيمته.أما لو لم تجد مفتاحًا مُطابقًا، سيتمّ طباعة رسالة تُشير إلى ذلك.يجب بعد ذلك تعيين قيمة للمفتاح، ليكون حاضرًا عند السؤال عن البيانات/القيمة في المرّة القادمة، ستجد memcached البيانات/القيمة المُعيّنة له. <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); $result = $mem->get("blah"); if ($result) { echo $result; } else { echo "No matching key found. I will add that now"; $mem->set("blah", "I am data! I am held in memcached") or die("Couldn't save anything to memcached..."); } ?>أصبحت الشيفرة/السكريبت جاهزةً في هذه المرحلة، وبزيارة الصّفحة باستخدام المُتصفّح، سيتمّ رؤية التّالي: http://server_domain_name_or_IP/cache_test.phpستظهر النتيجة على الشكل التّالي في الزيارة الأولى، والّتي تُشير إلى عدم وجود مُفتاح مُطابق، على أنّ تتم إضافته إلى حين الطلب اللّاحق: وستكون النتيجة على الشكل التّالي في الزيارة التّالية: يُوضّح المثال السابق، كيف استطاعت خدمة memcached على تخبئة المعلومات المُعيّنة كما هو مُبرمجٌ لها أن تفعل. اختبار تخبئة قيم قواعد البيانات بشكل مؤقّت (Caching Database Values)أصبح الآن من المُمكن تخزين البيانات في الذّاكرة المُخبئة، ومن المُمكن توضيح المزيد من الأمثلة الواقعيّة، وذلك بالتّطرّق إلى تخبئة النتائج المُستعلمة من قواعد البيانات بشكل مؤقّت في الذّاكرة. إنشاء عيّنة بيانات في MySQLسيتمّ إعداد قواعد البيانات بشكلٍ مُلائم أولًا، وذلك قبل أنّ يتمّ إنشاء هذه العيّنة سيتمّ الاتصال بقواعد البيانات كمُستخدِم مُدير مع إدخال كلمة المرور المُعيّنة خلال تنصيب MySQL، ليتمّ استحضار موجّه الأوامر الخاصّ بـ MySQL: mysql -u root -pسيتمّ إنشاء قاعدة بيانات ليتمّ الاختبار عليها، وبعد ذلك البدء في استخدامها: CREATE DATABASE mem_test; USE mem_test;سيتمّ إنشاء مُستخدِم بالاسم “test”، وبكلمة مرور “testing”، وبصلاحيات وصول إلى القاعدة الّتي تمّ إنشاؤها: GRANT ALL ON mem_test.* TO test@localhost IDENTIFIED BY 'testing123';سيتمّ بعد ذلك إنشاء جدول يحتوي على سجل وحيد، سيحمل الجدول الاسم “sample_data”، مُحتويًا على عمودان، نصيّ وعددي. CREATE TABLE sample_data (id int, name varchar(30)); INSERT INTO sample_data VALUES (1, "some_data");تمّ بذلك إنشاء بنية قاعدة البيانات اللازمة، وأصبح بالإمكان الخروج من موجّه الأوامر الخاصّ بـ MySQL: exitكتابة شيفرة PHP لتخبئة بيانات MySQLأصبح من المُمكن كتابة شيفرة تُحاكي بيئة عمل تطبيق حقيقي في البيئة الحيّة، وذلك بعد أنّ أصبحت قواعد البيانات مجهّزة بشكل مُناسب. ستقوم الشيفرة بالبحث عن البيانات في الذّاكرة المُخبّئة والعودة بالبيانات المُتوفّرة، ولكن عند عدم إيجاد أي بيانات، سيتمّ الاستعلام من قواعد البيانات نفسها، ومن ثمّ تخزين النتائج في الذّاكرة المخبّئة من أجل الاستعلامات المُستقبليّة. سيتمّ كتابة الشيفرة في المسار الجذر الخاصّ بخادوم الوب، وسيحمل الملفّ الّذي يحتوي هذه الشيفرة الاسم database_test.php: sudo nano /var/www/html/database_test.phpستبدأ سُطور الشيفرة بشكل مُشابه إلى الشيفرة السابقة، وذلك بإنشاء حالة وإخبارها بمكان خدمة memcached على الخادوم، كما تمّ الأمر في المثال السابق: <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); ?>ستكون الخطوة التّالية هي تحديد كيف لـ PHP الاتصال بقواعد بيانات MySQL، وذلك بتمرير بيانات الاعتماد/الدخول الخاصّة بالمُستخدِم المُنشئ سابقًا، ومن ثُمّ تحديد أيًا من قواعد البيانات يجب استخدامها: <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); mysql_connect("localhost", "test", "testing123") or die(mysql_error()); mysql_select_db("mem_test") or die(mysql_error()); ?>سيتوجّب في الخطوة التّالية بناء الاستعلام، الّذي وظيفته جلب البيانات الموجودة في جدول قاعدة البيانات، وتخزينه في المُتغيّر $query. سيتمّ بعد ذلك إنشاء مُتغيّر $querykey ليخزّن المفتاح الذي ستستخدمه memcached لاسترجاع المعلومات سيتمّ بناء هذا المُفتاح بالجمع بين الكلمة “KEY”، وبعثرة (hashing) من نوع md5، ويضمن ذلك أن كلَّ مُفتاح هو مُفتاح فريد عن الآخر، فعندما يتمّ استخدام هذا الأسلوب مع مجموعة كبيرة من البيانات، وليس كما هو حال هذا المثال المُبسّط، سيضمن ذلك أن أي استعلام مُماثل سيُنتج نفس المُفتاح للطلبات المُستقبليّة. <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); mysql_connect("localhost", "test", "testing123") or die(mysql_error()); mysql_select_db("mem_test") or die(mysql_error()); $query = "SELECT ID FROM sample_data WHERE name = 'some_data'"; $querykey = "KEY" . md5($query); ?>سيتمّ في الخطوة التّالية إنشاء المُتغيّر querykey، ليتمّ تعيينه إلى المُتغيّر $result في حال توفّره. <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); mysql_connect("localhost", "test", "testing123") or die(mysql_error()); mysql_select_db("mem_test") or die(mysql_error()); $query = "SELECT name FROM sample_data WHERE id = 1"; $querykey = "KEY" . md5($query); $result = $mem->get($querykey); ?>سيتمّ عمل اختبار منطقي (جملة شرطيّة)، والّذي من شأنه أن يُحدّد ماذا سيحدث عند وجود نتائج (جواب الشرط true) في الذّاكرة المُخبّئة، وفي حال ذلك سيتمّ طباعة البيانات المسحوبة، وإخبار المُستخدم أنه قد تمّ استدعاء البيانات من الذّاكرة المُخبّئة بشكل مُباشر: <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); mysql_connect("localhost", "test", "testing123") or die(mysql_error()); mysql_select_db("mem_test") or die(mysql_error()); $query = "SELECT name FROM sample_data WHERE id = 1"; $querykey = "KEY" . md5($query); $result = $mem->get($querykey); if ($result) { print "<p>Data was: " . $result[0] . "</p>"; print "<p>Caching success</p><p>Retrieved data from memcached!</p>"; } ?>سيتمّ في الخطوة التّالية كتابة الحالة البديلة، وستكون عند عدم وجود نتائج (جواب الشرط false)، ولذلك يجب استخدام الاستعلام الذي تمّت صياغته سابقًا لسؤال MySQL عن البيانات، ومن ثمّ تخزينها داخل المُتغيّر $result، والذي سيكون على شكل مصفوفة (array). أصبح من المُمكن بعد ذلك إضافة النتائج إلى الذّاكرة المُخبّئة، بعد توفّر نتيجة الاستعلام، لتكون هذه البيانات مُخبّئة للطلب التّالي، ولذلك سيتمّ تمرير ثلاثة مُعاملات لتعيين الذّاكرة المُخبّئة: المفتاح الّذي سيتمّ استخدامه كإشارة مرجعيّة للبيانات، وهو نفس المُفتاح المُنشئ بالمُتغيّر $querykey.البيانات نفسها، وهي مخزّنة في المُتغيّر $result بعد إجراء استعلام MySQL.مُدّة التخبئة بالثانية.سيتمّ تخزين التخبئة لمُدّة ثوانٍ قليلة، في البيئة الحقيقية، سيكون من المُفيد تخبئة المُحتوى لمدّة أطول، ربما دقائق عدّة، خاصّة إذا كان المُحتوى لا يتغيّر بشكل كبير، ولكن لأغراض التجربة، سيتمّ الالتزام بهذه الثواني بدلًا من إعادة تشغيل خدمة memcached لتكملة الاختبار. بعد ذلك، سيتمّ طباعة رسالة بنتائج الاستعلام، مع إخبار المُستخدِم بطريقة الحصول على البيانات: <?php $mem = new Memcached(); $mem->addServer("127.0.0.1", 11211); mysql_connect("localhost", "test", "testing123") or die(mysql_error()); mysql_select_db("mem_test") or die(mysql_error()); $query = "SELECT name FROM sample_data WHERE id = 1"; $querykey = "KEY" . md5($query); $result = $mem->get($querykey); if ($result) { print "<p>Data was: " . $result[0] . "</p>"; print "<p>Caching success</p><p>Retrieved data from memcached!</p>"; } else { $result = mysql_fetch_array(mysql_query($query)) or die(mysql_error()); $mem->set($querykey, $result, 10); print "<p>Data was: " . $result[0] . "</p>"; print "<p>Data not found in memcached.</p><p>Data retrieved from MySQL and stored in memcached for next time.</p>"; } ?>اكتملت الشيفرة بهذا الشكل، وبالمُختصر ما سيحدث هو مُحاولة الحصول على البيانات من الذّاكرة المُخبّئة، واستعراضها عند توفرها، وفي حال الفشل في الحصول على البيانات من الذّاكرة المُخبّئة، سيتمّ التوجّه مُباشرةً لاستعلام قواعد البيانات، ومن ثُمّ تخزين النتائج للطلب التّالي ولفترة ثواني معدودة. اختبار الشيفرةتمّ الانتهاء من كتابة الشيفرة، وأصبح بالإمكان اختبارها، وذلك عبر التوجّه إلى المُتصفّح واستعراض ملفّ الشيفرة: http://server_domain_name_or_IP/database_test.phpيجب أنّ تكون المُخرجات في الزيارة الأولى على الشكل التّالي: ما أن يتمّ تحديث الصفحة، وقبل انقضاء الثواني العشر، حتّى تَعرض الصفحة رسالة مُختلفة كما في التّالي: ما إنّ تنقضي الثواني العشر، ستنتهي صلاحيّة المُحتوى المُخبّئ، وسيُحذف من الذّاكرة المُخبّئة (memcached)، وستُعرض الرسالة الأولى الّتي تُشير إلى أنّ البيانات قد تمّ استدعاؤها من قاعدة البيانات بدلًا من الذّاكرة المُخبّئة. كلمات أخيرةيُفترض الآن وبعد الانتهاء من هذا الشرح الحصول على فهم كامل لكيفيّة إعداد الذّاكرة المُخبّئة، وكيف من المُمكن الحفاظ على أداء خادوم الوب، بتجنيبه من إجراء استعلامات مٌتكرّرة على قواعد البيانات للحصول على نفس المُحتوى. يجب الانتباه إلى أنّ الشيفرة المُستخدمة في هذا الدليل هي فقط لأغراض الشرح، ولتُعطي فكرة واضحة عن آلية العمل وكيفيّة بناء منظومة كاملة وصحيحة تعتمد على الذّاكرة المُخبئة، واستعلام قواعد البيانات في حال فشلها. ترجمة – وبتصرّف – للمقال How To Install and Use Memcache on Ubuntu 14.04 لصاحبه Justin Ellingwood.1 نقطة
-
يُساهم إضافة بعض التحريك (animation) عندما تتغيّر أجزاء صفحة الويب، في مُساعدة المُستخدِم على إدراك ما الّذي يحدث في الصّفحة، فيُمكن للتحريك أنّ يَدل على وصول مُحتوى جديد، أو لفت نظره إلى المُحتوى الّذي هو طور الإزالة. ستتناول هذه المقالة كيف يُمكن للتحريك المُساعدة على تقديم مُحتوى جديد، وذلك عبر إظهار وإخفاء عناصر قائمة من القائمة الخاصّة بهم. See the Pen WvjoXa by Walid (@01walid) on CodePen.1 نقطة
-
كما ذكرنا في الدرس السابق (مدخل إلى برمجة إضافات ووردبريس) من هذه السلسلة فإن الخطّافات تعد من أهم الخصائص التي يوفرها ووردبريس والتي جعلته مرنًا flexible وقابل للتمدد extensible بصورة قل أن تجد لها نظيرًا في برمجيات الويب حيث يعتمد على توفير نقاط ربط تمكن المطورين من تنفيذ الدّوال التي يريدونها أثناء دورة حياة ووردبريس، وذلك باعتماده على معمارية Event Driven. يوفر ووردبريس للمطورين الاستفادة من هذه الميزة من خلال ما يعرف بالواجهة البرمجية للإضافة Plugin API والتي نحن بصدد الإطلاع على أهم الدّوال التي توفرها في جانبي الإجراءات Actions والمُرشّحات Filters وبعض الأمثلة التوضيحية التي ترسخ فهم هذه الدّوال والمفاهيم المتعلقة بها. الإجراءات في ووردبريسالدالة add_actionذكرنا سابقًا أنه يمكنك إضافة إجراء لحدث معين من خلال الدالة add_action لكننا لم نذكر الصّيغة العامة للدالة وتفاصيل المعاملات التي تقبلها، وهذه هي الصّيغة العامة للدّالة: add_action( $hook, $function_to_add, $priority, $accepted_args );حيث: $hook : هو الحدث الذي تريد أن تربط به الدالة الخاصة بك، أي الحدث التي تريد أن تنفذ دالتك عند حدوثه. ويمكنك الإطلاع على الأحداث التي يوفرها ووردبريس من هنا.$function_to_add:اسم الدالة (الإجراء) التي تريد إضافتها للحدث أعلاه. أي التي ستنفذ عند حصول الحدث $hook .$priority (اختياري): هذه هي أولوية تنفيذ الدالة عند هذا الحدث، بمعنى أنه إذا كان هنالك أكثر من دالة تم إضافتها باستخدام الدالة add_action إلى نفس الحدث فإن قيمة هذا المعامل ستحدد أي الدّوال ستنفذ أولا وأيها ثانيا وهكذا. قيمة هذا المعامل يجب أن تكون من النوع int، وقيمته الافتراضية 10 والحدث ذو القيمة الأقل سينفذ أولا، بمعنى أن الدالة التي لديها قيمة 5 ستنفذ قبل الدالة التي لديها 20 وهكذا.$accepted_args (اختياري): عدد المعاملات التي تقبلها الدالة المطلوب تنفيذها $function_to_add، فمثلا الإجراء publish_post يقوم بتمرير معرف المقال الذي تم نشره للدالة المطلوب تنفيذها. وعادة يتم تحديد هذه من قبل الخطاف نفسه.استخدام add_action مع الفئات Classesأحيانًا قد تود استخدام الدالة add_action لإضافة دالة تابعة لفئة أو لكائن Object معين حينئذ ينبغي عليك استخدام add_action كما في المثال التالي: class MyPluginClass { public function __construct() { add_action( 'save_post', array( $this, 'myplugin_save_posts' ) ); } public function myplugin_save_posts() { // do stuff here... } } $mypluginclass = new MyPluginClass(); وتلاحظ أننا استخدمنا اسم الحدث save_post وفي المعامل الثاني أشرنا إلى الدالة من خلال المصفوفة array( $this, 'myplugin_save_posts' )حيث تشير $this إلى الكائن الحالي وmyplugin_save_posts تشير إلى اسم الدالة التي نود إضافتها للحدث. الدالة remove_actionتعُتبر دالة remove_action الدالة العكسية للدالة add_action حيث تقوم بحذف الدالة التي تريدها من الحدث الذي تريده وتأخذ الصّيغة العامة التالية: remove_action( $tag, $function_to_remove, $priority );حيث: $tag : اسم الحدث الذي تم إضافة الدالة إليه$function_to_remove : الدالة التي تود حذفها من الحدث $tag$priority (اختياري): أولوية الدالة التي تريد حذفها والتي تم تعريفها مسبقا عند إضافة الدالة إلى الحدث عن طريق add_actionالدالة has_actionتستخدم دالة has_action لاختبار إذا كانت دالة معينة قد أضيفت إلى حدث معين، والصّيغة العامة لهذه الدالة هي: has_action( $tag, $function_to_check )حيث $tag : الحدث الذي تود معرفة إذا ما كانت الدالة قد أضيفت إليه.$function_to_check : الدالة التي تود معرفة إذا ما كانت أضيفت إلى الحدث $tag أما لا.القيمة المرجعة من الدالة: ترجع هذه الدالة قيمة false إذا كانت الدالة $function_to_check غير مضافة للحدث $tag وترجع أولوية تنفيذها priority إذا كانت مضافة للحدث $tag.الدالة do_actionيستخدم ووردبريس الدّالة do_action لتحديد المكان الذي سينفذ فيه الإجراء، وبالتالي في هذا المكان الذي تتواجد فيه الدالة يقوم بتنفيذ كل الدّوال التي تم إضافتها لهذا الحدث المعين، وتأخذ الصّيغة العامة do_action( $tag, $arg );حيث: $tag : اسم الإجراء الذي تود تنفيذه. وكما أسلفنا فإن هذه الدالة عند مناداتها ستنفذ كل الدّوال التي تم إضافتها لهذا الحدث $tag.$arg (اختياري): المعامل الذي يتم تمريره للدوال المرتبطة بالحدث $tagهذه الدالة في الأساس يستخدمها ووردبريس ليحدد مكان تنفيذ الأحداث التي يوفرها لكن يمكنك أيضا استخدامها لإنشاء أحداث خاصة بك في حالة أردت مثلا أن تتيح للمطورين نقاط معينة داخل إضافتك (أو قالبك) لينفذوا فيها دوالهم الخاصة، وبهذه الطريقة يمكن إنشاء إضافات قابلة لتشتغل عليها إضافات أخرى أو ما يمكن أن نطلق عليه اسم Extensions وهنالك عدة أمثلة لإضافات ناجحة توفر هذه الميزة نذكر منها على سبيل المثال إضافة Bbpress المشهورة وكذلك إضافة Woocommernce حيث أن هاتين الإضافتين تعمل فوقهما عدة إضافات أخرى تزيد من مميزاتهما، وتقريبا معظم إضافات ووردبريس توفر مجموعة من الخطافات hooks للمطورين، لذلك في حالة فكرت في إنشاء إضافة لنشرها في مجتمع ووردبريس من الجيد أن تجعل نظرتك مستقبلية وتوفر بعض الخطافات للمطورين. يحسن التنبيه إلى أن الدالة do_action يمكن أن تستدعى بأكثر من معامل بالصّيغة التالية do_action( $tag, $arg_a, $arg_b, $etc );وكما أسلفنا في تعريف معاملات الدالة add_action تستطيع تحديد المعاملات التي تقبلها الدالة المضافة إلى add_action، ولتحديد القيمة هناك عليك أن ترى عدد المعاملات الموجودة هنا في do_action. أمثلة عملية للخطافات في ووردبريسالتعامل مع ملفات جافاسكربتفي ووردبريس إذا أردت أن تضيف ملف جافاسكربت للموقع فإن أفضل طريقة -غالبًا- هي استخدام الحدث wp_enqueue_scripts والذي تستطيع من خلاله إضافة ملفاتك إلى بقية ملفات جافاسكربت (أو CSS) وتترك لووردبريس مهمة ربطها في ترويسة الموقع وهذا ما تفعله الشيفرات التالية function my_scripts_method() { wp_enqueue_script( 'myscript', // اسم ملف جافاسكربت plugins_url( '/js/newscript.js' , __FILE__ ) // مكان ملف الجافاسكربت ); } add_action( 'wp_enqueue_scripts', 'my_scripts_method' ); لاحظ أننا في السطر الأخير أضفنا الدالة my_scripts_method إلى الحدث wp_enqueue_scripts وبالتالي سيتم تنفيذ هذه الدالة عندما ينادي ووردبريس الإجراء wp_enqueue_scripts، أما الدالة نفسها فقد قمنا بداخلها باستخدام دالة أخرى وهي الدالة wp_enqueue_script والتي تتولى مهمة إضافة السكربت حيث تأخذ اسم السكربت (الذي تريده) ومكانه وتتولى بقية المهمة. إضافة قائمة جانبية Sidebar جديدةهذا المثال متعلق أكثر بالقوالب لكن لا مانع من أن نطلع عليه لتعرف أن مفهوم الأحداث يمتد حتى إلى برمجة القوالب، وفي هذا المثال سنقوم بإضافة قائمة جانبية Sidebar جديدة للقالب من خلال الشيفرات التالية function create_my_widget() { register_sidebar(array( 'name' => __( 'My Sidebar'), // اسم القائمة الجانبية 'id' => 'my_sidebar', // معرف القائمة الجانبية )); } add_action( 'widgets_init', 'create_my_widget' );لاحظ أننا في السطر الأخير أضفنا الدالة create_my_widget إلى الحدث widget_init والذي يستخدمه ووردبريس لتعريف جميع القوائم الجانبية، وداخل الدالة قمتا باستخدام دالة أخرى وهي الدالة register_sidebar والتي تأخذ اسم القائمة الجانبية ومعرفها الفريد وتتولى مهمة تسجيلها. إنشاء خطاف إجراء action hook خاص بكلعمل ذلك كل ما عليك هو إضافة الدالة do_action إلى المكان الذي تود أن تنفذ فيه الدّوال التي تربط بهذا الخطاف وبالطبع ستمرر للدالة do_action الاسم الذي تود أن تسمي به الخطاف، بالصّيغة التالية do_action('my_action_hook');وبالطبع يمكنك إضافة بقية المعاملات التي ذكرناها في تعريف الدالة do_action كلما اقتضت الحاجة. المرشحات في ووردبريسبنفس الطريقة التي اتبعناها في الأحداث أعلاه سنتعرف على أهم الدّوال الخاصة بالمرشحات التي يوفرها ووردبريس الدالة add_filterتستخدم دالة add_filter لإضافة مرشح جديد لأحد المتغيرات وتأخذ الصّيغة العامة: add_filter( $tag, $function_to_add, $priority, $accepted_args );لاحظ أن الدالة شبيهة جدا بالدالة add_action وكذلك المعاملات هي نفسها تقريبا، لكن لا باس من إعادة تعريفها: $tag: اسم خطاف المرشح filter hook الذي تود أن تضيف له الدالة $function_to_add لكي تعدل على قيمة المتغير الذي يمثله الخطاف.$function_to_add: الدالة التي ستتولى عملية التعديل على المتغير الذي يشير اليه الخطاف $tag. وهذه الدالة تقوم بإرجاع القيمة الجديدة للمتغير الذي تستقبله.$priority : أولوية التنفيذ للدوال المضافة لنفس الخطاف، حيث تحدد قيمة هذا المتغير ترتيب تنفيذ الدّوال المرتبطة بهذا المرشح. ويأخذ المتغير $priority قيمة من النوع int.الدّوال التي لها $priority أقل تنفذ أولا، وإذا كان هنالك أكثر من دالة لها نفس قيمة المتغير $priority حينئذ يتم تنفيذها على حسب ترتيب تعريفها.$accepted_args: عدد المعاملات arguments التي تقبلها الدالة $function_to_add. حيث يمكن تعريف أكثر من معامل للخطاف عند تنفيذه من خلال apply_filters كما سنرى في الفقرات القادمة. وعدد المعاملات عادة يتم تحديد من خلال الخطاف نفسه.الدالة remove_filterكما يظهر من اسمها فإن remove_filter الدالة العكسية لـadd_filter وتأخذ الصّيغة التالية remove_filter( $tag, $function_to_remove, $priority );حيث: $tag: اسم خطاف المرشح الذي تود إزالة الدالة عنه.$function_to_remove: اسم الدالة التي تود إزالتها.$priority (اختياري): أولوية الدالة التي تود إزالتها كما تم تعريفها باستخدام add_filterالدالة has_filterتقوم الدّالة has_filter باختبار ما إذا كان هنالك دالة تم إضافتها لخطاف معين، وتأخذ الصّيغة التالية has_filter( $tag, $function_to_check ); حيث: $tag: اسم خطاف المرشح.$function_to_check: اسم الدالة التي تريد التأكد من وجودهاالدالة apply_filtersتقوم دالة apply_filters بتنفيذ الدّوال المرتبطة بالخطاف الذي يمرر لها كمعامل وتأخذ الصّيغة العامة apply_filters( $tag, $value, $var ... ); حيث: $tag : اسم الخطاف المراد تنفيذ الدّوال التي تم إضافتها له من خلال add_filter$value : القيمة الأصلية للمتغير والتي يتم تغيرها من خلال الدّوال المربوطة بالخطاف، أي القيمة التي ترشح من خلال الدّوال.$var (اختياري): متغير إضافي أو أكثر يتم تمريرها إلى الدّوال المرتبطة بالخطاف $tag وكنا قد ذكرنا أنه بالإمكان تحديد عدد المتغيرات التي تستقبلها الدالة المربوطة بالخطاف من خلال المعامل $accepted_args للدالة add_filter حيث تقوم بعد المعاملات هنا وتحديد عددها عند إضافة أي دالة لهذا الخطاف.أمثلة للمرشحات في ووردبريستعديل عدد كلمات المقتطففي هذا المثال سنستخدم الخطاف excerpt_length الذي يوفره ووردبريس للتعديل على قيمة طول نص المقتطف function excerpt_length_example( $words ) { return 15; } add_filter( 'excerpt_length', 'excerpt_length_example' );قمنا بإنشاء دالة ترجع القيمة الجديدة لطول المقتطف (15)، ثم بعد ذلك أضفناها إلى المرشح excerpt_length باستخدام الدالة add_filter إنشاء خطاف مرشح filter hook لأحد متغيراتكفلنفرض أنك تريد أن تتيح للمطورين إمكانية التعديل على نص معين قبل عرضه، يمكن أن توفر لهم خطاف مرشح ليضيفوا له الدّوال التي يودون أن تقوم بعميلة التعديل على النص، وذلك من خلال تعريف المتغير بالصّيغة التالية $text = apply_filters("my_text", $text);بهذه الصورة فإنه سيقوم ووردبريس بتطبيق جميع الدّوال المضافة للخطاف my_text على المتغير $text في الجانب الأيمن من المعادلة قبل إسناده للمتغير في الجانب الأيسر، وهذا هو المطلوب. أسماء الخطافات المتغيرة Variable Hook Namesالخطافات التي اطلعنا عليها أعلاه ليست كل ما يوفره ووردبريس بل هنالك المزيد من المرونة المتاحة من خلال أسماء الخطافات المتغيرة فمثلا لنلقى نظرة على الخطاف publish_post وهو عبارة عن خطاف حدث action hook يوفره ووردبريس ويتم تنفيذ الدّوال المرتبطة به كلما تم نشر publish مقال post وهذا في الحقيقة خطاف جميل جدا يمكنك من تنفيذ أي دالة تريدها عندما يتم نشر أي مقال لكن ماذا إن أردت تنفيذ دالة معينة عند نشر نوع مقالات مخصص ولتكن مثلا منتجات، أي نريد تنفيذ الدالة my_function عندما يتم نشر publish منتج product، هذا الأمر انتبه له المطورون ولذلك قاموا بإضافة هذا الخطاف ذو الاسم العام {$new_status}_{$post->post_type} وكما ترى فإنه يوفر مرونة عالية جدا حيث يمكنك اختيار الحالة التي تريد التنفيذ عنها وأصبح الأمر ليس مقتصرا على النشر فقط publish بل يمكنك مثل تنفيذ شئ معين عند حذف delete مقال أو عموما تغير حالة المقال إلى أي حالة {$new_status}. كما أن الأمر لم يعد مقتصرا على المقالات فقط بل يمكنك تحديد نوع المقال {$post->post_type} وهكذا اصبح الخطاف publish_post حالة خاصة من خطاف أعم له الاسم العام {$new_status}_{$post->post_type}. هذا مثال واحد فقط لأسماء الخطافات المتغيرة وهنالك خطافات متغيرة غيره يوفرها ووردبريس لإتاحة المزيد من المرونة للمطورين. خاتمةرغم أننا أردنا أن نلقي نظرة شاملة على الخطافات لكن ما زال هنالك المزيد لاكتشافه لكن ما تناولناه يعتبر مغطيا بصورة كبيرة لأهم الأشياء التي تحتاج إليها عند بداية مشوارك مع تطوير إضافات ووردبريس وسنتركك لتكتشف المزيد بنفسك عند التعمق في عالم ووردبريس.1 نقطة
-
من أهم الأمور التي ينبغي على مطور ووردبريس الإلمام بها ومعرفتها هي قاعدة بيانات ووردبريس حيث أنها المستودع الرئيسي للبيانات في نظام إدارة المحتوى كما أنها تعكس التركيبة الخاصة بووردبريس وأهم محتوياته الأساسية وطريقة تمديده بصورة مثالية ويظهر هذا جليًَا عند اطلاعنا على جداول meta في الفقرات القادمة. في هذا المقال سنحاول تقديم نبذة عن جداول قاعدة بيانات ووردبريس ودور كل جدول بالإضافة لأهم محتوياته وارتباطه بالجداول الأخرى، بالإضافة إلى ذكر بعض الطرق التي يوفرها ووردبريس للتعامل مع البيانات. جداول قاعدة بيانات ووردبريس انقر على الصورة لمُشاهدتها بحجمها الكامل هذا التفصيل الذي سنذكره قد لا تجده حتى في التّوثيق الرّسمي لووردبريس وبالتالي سيكون مرجعًا مُفيدًا لمن أراد دراسة قاعدة البيانات بتعمّق. جدول wp_optionsتطرقنا من قبل لهذا الجدول في مقال استقبال وحفظ الخيارات حيث ذكرنا أنّ ووردبريس يستخدمه لحفظ الخيارات الخاصة به وكذلك يتيح لك حفظ خيارات القوالب أو الإضافات التي تبرمجها فيه من خلال توفير الواجهة البرمجية للإعدادات والتي أسهبنا في الكلام عنها في المقال آنف الذّكر. يحتوي هذا الجدول على أربعة أعمدة وهي option_id، option_name, option_value, autoload وهي نفس المعاملات التي تقوم بتمريرها للدّالة add_options التي تكلّمنا عنها سابقا. لا يرتبط هذا الجدول بصورة مباشرة بأيّ من الجداول الأخرى في قاعدة البيانات. جدول wp_usersكما هو واضح من اسمه خاص بحفظ بيانات المستخدمين لموقع ووردبريس بجميع رتبهم سواء تعلّق الأمر بالمدير admin أو بغيره من الرّتب. هذا الجدول مكون من عشرة أعمدة وهي: ID: وهو المعرف الخاص بالمستخدم، ويجب أن يكون فريدًا وهو المفتاح الرئيسي للجدول Primary key. user_login: اسم المستخدم أو اسم الدخول. user_pass: كلمة مرور المستخدم وتحفظ مشفرة في قاعدة البيانات. user_nicename: الاسم اللطيف للمستخدم. user_email: البريد الإلكتروني للمستخدم. user_url: الموقع الإلكتروني للمستخدم. user_registered: تاريخ تسجيل المستخدم. user_activation_key: المفتاح المستخدم عند استعادة كلمة المرور، يقوم ووردبريس بتوليده عند طلب استعادة المرور ويقوم بمسحه بعد استعماله مباشرة. user_status: تم التخلي عن هذا العمود deprecated وربما يتم إسقاطه في النسخ القادمة من ووردبريس. display_name: اسم العرض للمستخدم والذي يظهر في واجهة الموقع. جدول المستخدمين مرتبط بعدة جداول في قاعدة بيانات ووردبريس حيث يرتبط بجدول المقالات لأن كل مقال يكون منسوبًا لكاتب (مستخدم) معين، وكذلك مرتبط بجدول التّعليقات لنسبة كل تعليق لكاتبه إن كان من المُسجًّلين في الموقع، وأخيرًا يرتبط بجدول wp_usermeta وهو الجدول التالي في شرحنا. جدول wp_usermetaكما رأينا فإن جدول المستخدمين مكون من أعمدة مُحدّدة بالتّالي عندما تحتاج لإضافة بيانات معينة للمستخدم مثل روابطه في الشبكات الاجتماعية مثلًا ستضطر إلى إضافة المزيد من الأعمدة في جدول المستخدمين لكن هذه الطريقة غير عملية لذلك يأتي جدول wp_usermeta ليُمكّنك من إضافة المزيد من البيانات meta للمستخدمين بدون التعديل على تركيبة الجداول (الأعمدة) وبإضافة المزيد من الصفوف لهذا الجدول. يحتوي هذا الجدول على أربعة أعمدة وهي umeta_id: وهو المعرف الخاص بهذه الـmeta التي تريد إضافتها وهو فريد حيث أنه المفتاح الرئيسي لهذا الجدول. user_id: معرف المستخدم الذي تعود له هذه البيانات meta وهو عبارة عن مفتاح أجنبي foreign key يعود إلى جدول wp_user وبهذه الطريقة يتم الربط بين الجدولين كما هو معلوم في قاعدة البيانات العلائقية Relational Database. meta_key: مفتاح البيانات meta (اسمها) والذي يتم استدعاؤها به برمجيًّا حيث أن لكل meta مفتاح وقيمة كما هو في الخيارات. meta_value: قيمة البيانات meta. هذه الطريقة المستخدمة في إضافة المزيد للبيانات للمستخدمين بدون التعديل على جدول wp_user وذلك من خلال إتاحة جدول wp_usermeta مستخدمة أيضا مع المقالات post والتعليقات comment حيث يستخدم جداول غير الجداول الأساسية لإضافة بيانات غير البيانات الأساسية لكل من المقالات والتعليقات كما سنرى في الفقرات التالية، ولعل الفكرة العامة لهذه الطريقة وضحت من خلال الجدول الأساسي wp_user وجدوله المساعد wp_usermeta. جدول wp_postsيمكن أن نعتبر هذا الجدول هو الجدول الأساسي في ووردبريس حيث أنه الرّكيزة الأساسية لأنواع المقالات والتي يمكن أن نعتبرها المحتويات في نظام إدارة المحتوى، أي أنها العنصر الأساسي الذي تم من أجله إنشاء النظام. تجدر الإشارة أن هذه الجدول ليس مُختصًّا بالمقالات فقط بل بكل أنواع المقالات والتي تشمل الصفحات أيضا وأي نوع مقالات أخر يتم إنشاؤه، وإن كنت لم تتطلع من قبل على أنواع المقالات المخصصة فأشير إليك بقراءة هذا المقال المختصر عن أنواع المقالات المُخصّصة في ووردبريس. يحتوي هذا الجدول على ثلاثة وعشرين عمودًا وإليك تفاصيلها ID: معرف فريد للمقال. post_author: كاتب المقال. post_date: تاريخ المقال. post_date_gmt: تاريخ المقال بتوقيت جرينتش. post_content: محتويات المقال (النّصّ الفعلي للمقال). post_title: عنوان المقال. post_excerpt: مُقتطف المقال (نصّ اختياري يمكن للمستخدم أن يدخله). post_status: حالة المقال (منشور، مسودة، تحت المراجعة، ... الخ). comment_status: حالة التّعليقات (هل التعليقات مفتوحة أم مغلقة). ping_status: حالة التّنبيه (هل التنبيهات مفعلة أم لا). post_password: كلمة مرور للمقال (في حالة كان مغلقا بكلمة مرور). post_name: رابط المقال أو ما يسمى بالاسم اللطيف. to_ping: روابط المقالات التي سيتم تنبيهها (أي المشار لها في المقال الحالي) وهذه لي حالة أن المقال لم ينشر بعد. pinged: روابط المقالات التي تم تنبيهها وهذه في حالة أن المقال قد تم نشره. post_modified: تاريخ آخر تعديل للمقال. post_modified_gmt: تاريخ آخر تعديل للمقال بتوقيت جرينتش. post_content_filtered: تستخدم لتخزين نسخة cache لـpost_content المشار لها سابقا (عادة بعد تطبيق الخطافات المرتبطة بالمُرشح the_content عليها). post_parent: معرف المقال الأب لهذا المقال (في حالة كان نوع المقالات هرمي hierarchical مثل الصّفحات. guid: اختصار لـGlobal Unique Identifier وهي عبارة عن الرابط الحقيقي للمقال. في حالة الصفحات يكون هذا هو الرابط الفعلي إمّا في حالة الملفّات المرفقة attachments يكون هذا هو الرابط للملف المرفق. menu_order: قيمة عددية (0، 1، 2، ...) تُحدّد ترتيب المقال وتعمل مع أنواع المقالات الهرمية مثل الصفحات وليس المقالات العادية. post_type: نوع المقال (مقال، صفحة، منتج، ملف مرفق، ..الخ). post_mime_type: تعمل فقط مع الملفات المرفقة attachments حيث تحتوي على نوع الميديا للملف MIME type. comment_count: عدد التعليقات مضاف إليه عدد التنبيهات pings والتعقيبات trackbacks. يرتبط جدول المقالات wp_posts مع مجموعة من الجداول وهي جدول wp_user: كما تم الإشارة إلى هذا الأمر سابقا حيث أن لكل مقال كاتب. جدول التعليقات wp_comments وهذا واضح ليربط كل مقال بالتّعليقات عليه. جدول wp_term_relationships وسيتم شرح هذا في الفقرات القادمة. جدول wp_postmeta: وهو الجدول التّالي في شرحنا جدول wp_postmetaكما هو الأمر بالنسبة للمستخدمين قد تحتاج لإضافة المزيد من البيانات للمقال غير البيانات الأساسية الموجودة في جدول المقالات wp_post فمثلا إذا كان لديك نوع مقالات يُسمّى منتجات قد تحتاج لإضافة بيانات إضافية لكل منتج مثل السّعر والكمّيّة المُتوفّرة وغيرها من البيانات وكما هو الحال بالنسبة للمستخدمين يتم استخدام جدول إضافي وهو جدول wp_postmeta لإضافة مثل هذه البيانات وربطها بالمقال المعين. يتكون هذا الجدول من أربعة حقول وهي meta_id: وهو المعرف الخاص بهذه الـmeta التي تريد إضافتها وهو فريد حيث أنه المفتاح الرئيسي لهذا الجدول. post_id: معرف المقال الذي تعود له هذه البيانات meta وهو عبارة عن مفتاح أجنبي foreign key يعود إلى جدول wp_post وبهذه الطريقة يتم الربط بين الجدولين. meta_key: مفتاح البيانات meta (اسمها) والذي يتم استدعاؤها به برمجيًّا حيث أن لكل meta مفتاح وقيمة كما هو في جدول wp_usermeta. meta_value: قيمة البيانات meta. يرتبط هذا الجدول بجدول المقالات wp_posts فقط. جدول wp_commentsوكما هو واضح من الاسم هذا هو الجدول الذي يتم فيه حفظ التعليقات، وله مجموعة من الحقول لن نتطرق لها الآن حتى لا نطيل الشرح كما أنه يمكنك الاطلاع عليها في الصورة أعلاه. يرتبط هذا الجدول بجدول المستخدمين wp_users إذا كان التعليق من مستخدم مسجل في الموقع، كما يرتبط بالطّبع بجدول المقالات حيث أن التعليق يعود لمقال معين حيث يكون معرف المقال ID هو المفتاح الأجنبي والذي يقوم بعميلة الربط. أيضا يرتبط هذا الجدول بجدول wp_commentmeta وهو الجدول التالي في الشرح. جدول wp_commentmetaومثل هذا الجدول والعلاقة التي تربطه بجدول wp_commetnts هو مثل العلاقة بين كل من wp_posts و wp_postmeta وكذلك wp_users و wp_usermeta. وأظن أن الصورة قد أصبحت واضحة بالنسبة لهذه الطريقة التي يستخدمها ووردبريس في الربط بين البيانات. جدول wp_linksيستخدم لحفظ البيانات المتعلقة بميزة الروابط links والتي أصبحت ملغية deprecated ورغم أنه يمكنك إعادة تفعيلها من خلال إضافة Link Manager إلا أننا لن نتطرق لشرح هذا الجدول (حتى الإضافة لم تحدث منذ أكثر من عامين). جدول wp_term_taxonomyهذا الجدول يُعرّف العلاقة بين ما يسمى بالفئات أو الأنواع Taxonomies والـterms الموجودة في الموقع. كل نوع Taxonomy عبارة عن طريقة لتنصيف المقالات وفق شيء معين، مثلا يأتي ووردبريس بعدة أنواع افتراضية منها التصنيفات والوسوم وهما للمقالات، لكن يمكنك إضافة المزيد من الأنواع لأي نوع من أنواع المقالات طالما أنه يدعمها. كل واحد من الأنواع (الوسوم مثلا) يحتوي على عدد من الـterms وهي عبارة عن الوسوم نفسها وكل وسم يحتوي على عدة مقالات أو يمكن أن نقول أن كل مقال له عدة وسوم وكذلك عدة تصنيفات وهكذا يكون المقال له عدد من الـterms. إذن يمكن أن نعتبر أن كل نوع Taxonomy عبارة عن أب أو طريقة لتجميع عدد من الـterms أو أن كل term يتبع لنوع معين. الصورة التالية توضح مفهومي الأنواع Taxonomies وكذلك الـTerms والعلاقة بينهما انقر على الصورة لمُشاهدتها بحجمها الكامل هذا الجدول يحتوي على ستة أعمدة وهي term_taxonomy_id: معرف العلاقة بين النوع Taxonomy والـterm. term_id: معرف الـterm (مثلا 1 أو 7). taxonomy: اسم النوع الذي يتبع له هذا الـterm (مثلا تصنيفات أو وسوم). description: وصف للعلاقة. parent: الأب لهذا الـterm. count: عدد المقالات في هذا الـterm. هذا الجدول يرتبط بجدولي wp_term_relationships وwp_terms التّاليين. جدول wp_termsهذا الجدول يعرف فيه الـterms (مثلا التصنيفات أو الوسوم) الموجودة في الموقع ويرتبط بالجدول السابق ليربط كل term مع النوع taxonomy الخاص به. يتكون هذا الجدول من أربعة عمدة هي term_id: معرف الـterm وهذا هو المفتاح الأساسي لهذا الجدول. name: اسم الـterm. slug: الاسم اللطيف للterm. term_group: هذا العمود مضاف لإضافة خاصية تسمح بوجود عدة مسميات لنفس الـterm لكن يبدو أن هذه الخاصية لم تدعم حتى الآن في ووردبريس، وهذا وفقا لأحدى الإجابات على موقع Stack Exchange . جدول wp_term_relationshipsلاحظ أنّه في الجدولين السّابقين كان التركيز على كل من الـterms والأنواع والعلاقة بينهما لكن حتى الآن ليس هنالك ربط فعلي بين التصنيفات والمقالات وعموما بين الـterms والمقالات، وهذه مهمة هذا الجدول حيث يربط المقالات مع معرف جدول wp_term_taxonomy والذي كما ذكرنا يعرف العلاقة بين الأنواع والـterm التابعة لها. يتكون هذا الجدول من أربعة أعمدة هي object_id: وهو معرف المقال الذي يراد ربطه بالـterm الخاص به وهذا هو المفتاح الأساسي لهذا الجدول. term_taxonomy_id: وهو عبارة عن مفتاح أجنبي عبارة عن المفتاح الأساسي في جدول wp_term_taxonomy. term_order: ترتيب الـterm. هذا الجدول بالطّبع يرتبط مع جدولي wp_posts وكذلك جدول wp_term_taxonomy وأيضا يرتبط مع جدول wp_links الذي ذكرنا أنه أصبح deprecated. التعامل مع قاعدة بيانات ووردبريسبالطبع يمكنك استخدام دوال my_sqli الأساسية المتاحة مع لغة php للتّواصل مع قاعدة البيانات أو حتى استخدام فئة PDO لكن يفضل دوما استخدام الطرق التي يقدمها ووردبريس ما دام ذلك مُمكنًا للحصول على توافقية أكبر للإضافة مع بقية مكونات ووردبريس بالإضافة للاستفادة من الفئات والتسهيلات التي يوفرها ووردبريس، وإليك طريقتين يُوفرهما ووردبريس للتعامل مع قاعدة بياناته: 1- استخدام الحلقة The Loopهذا المصطلح في ووردبريس يرمز لأحد أهم المكونات في ووردبريس والتي تستخدم بكثرة في تطوير قوالب ووردبريس، وهي ببساطة طريقة لجلب المقالات (أو أنواع المقالات المخصصة) وعرضها في القالب أو الإضافة وهذه هي الصورة العامة للحلقة <?php if ( have_posts() ) : // التحقق من وجود المقالات ?> <?php /* بدء الحلقة */ ?> <?php while ( have_posts() ) : the_post(); // تجهيز بيانات المقال ?> // هنا يتم استخدام وسوم القالب لجلب البيانات المتعلقة بالمقال الحالي // لاحظ أن ما بداخل الحلقة يتكرر في كل مرة حتى يصل إلى عدم تحقق الشرط في السطر الأول <?php endwhile; ?> <?php endif; ?>داخل الحلقة يتم استخدام بعض الدّوال التي يوفرها ووردبريس والتي تسمى بوسوم القالب Template Tags وهي عبارة عن دوال جاهزة تستخدم لجلب بيانات المقال والبيانات المرتبطة به مثل اسم الكاتب وتصنيفات المقال وغيرها من المحتويات. إذن استخدام الحلقة يوفر لنا إمكانية استخدام مجموعة كبيرة جدا من الدّوال الجاهزة التي توفر كثيرا من الجهد والوقت. يمكن أن نقول أن هنالك نوعان من الحلقات، الأولى هي الحلقة الأساسية التي مثلنا لها أعلاه والثانية يمكن إنشاؤها عن طريق إنشاء كائن Object من الفئة WP_Query بالصورة التالية: <?php $args = array(); // هنا يتم تعريف المعاملات التي ستمرر في الأسفل ?> <?php $query = new WP_Query($args); // تعريف الكائن الذي سيستخدم لجلب المقالات في الحلقة ?> <?php if ( $query->have_posts() ) : // التّحقق من وجود المقالات ?> <?php /* بدء الحلقة */ ?> <?php while ( $query->have_posts() ) : $query->the_post(); // تجهيز بيانات المقال ?> // ما بداخل الحلقة مشابه للحلقة الأساسية <?php endwhile; ?> <?php endif; ?>عند إنشاء الكائن تقوم بتمرير بعض المعاملات التي تحدد لووردبريس المقالات التي تود جلبها فمثلا قد ترغب بجلب جزئية فقط من المقالات والتي تُحقّق شرطًا مُعيّنًا مثل المقالات التّابعة لتصنيف مُعيّن وهكذا، وهذا أيضا ينطبق على أنواع المقالات المُخصّصة حيث تُمرر نوع المقالات المُخصّصة الذي تود جلبه بالإضافة لبقية المعاملات التي تود تمريرها. بالطبع فإن الحلقة محدودة على جلب البيانات المُتعلّقة بالمقالات وأنواع المقالات المُخصّصة بالتّالي لن تستخدمها إلا عندما تود جلب هذا النوع من البيانات وإلا فستنتقل إلى طريقة أخرى، لكن طالما أنّك تود جلب هذا النوع من البيانات فلا تعدل عن استخدام الحلقة أو على الأقل استخدام الدالة get_posts التي تشبه الحلقة في معاملاتها لكنها ترجع البيانات في شكل مصفوفة. 2- استخدام صنف wpdbهذا الصّنف يحتوي على مجموعة من الدّوال يستخدمها ووردبريس في التعامل مع قاعدة بياناته، ويمكنك الوصول لهذه الدّوال من خلال استخدام الكائن $wbdb وهو عبارة عن global object وهذا مثال لطريقة استخدامه global $wpdb; $results = $wpdb->get_results( 'SELECT * FROM wp_options WHERE option_id = 1', OBJECT );تستطيع من خلال هذا الكائن الوصول إلى مجموعة كبيرة من الدّوال التي يوفّرها الصّنف wpdb، كما تستطيع أيضا الوصول إلى متغيرات هذا الصّنف والتي من ضمنها جداول قاعدة البيانات التي ذكرناها سابقا. هل تفضل استخدام طريقة أخرى إذا كان الأمر كذلك فلا تنس التّحقق الجيد من البيانات قبل إدخالها واسترجاعها من قاعدة البيانات، حتى لا تحتوي الإضافة على ثغرات أمنية تسهل من اختراق الموقع. خاتمةيمكن أن نقول أننا أنهينا الجانب النّظري لبعض أساسيات ووردبريس لمطوري الإضافات، ولم يتبق لنا سوى تقديم مثال عملي لإضافة حقيقية يربط هذه المفاهيم التي تعرفنا عليها ويوضّحها بصورة أكبر، وهذا ما سنقدمه في المقال القادم إن شاء الله.1 نقطة
-
نحتاج في أحيانٍ كثيرة إلى إدخال تعديلات محدّدة على بعض الملفات، فمديرو نظم التشغيل يحتاجون أحيانًا إلى تعديل مجموعة فقط من عناوين الـ IP التي تنتمي إلى فئة ما، أو مجموعة من أسماء النطاقات domains (أو النطاقات الفرعية)، بل ربما احتاج أحدنا إلى تصحيح بعض الأخطاء الكتابية المتكرّرة في ملفٍ طويل، كحذف الأسطر الفارغة مثلًا، أو المسافات الفارغة في بدايات الأسطر، تغطّي التعابير النمطيّة Regular Expressions التعامل مع هذا النوع من المشاكل. التعابير النمطيّة هي أسلوب يُستخدم لوصف النصوص والتعرّف عليها من خلال مطابقتها (أو عدم مطابقتها) مع رموز محدّدة، وتنتشر تطبيقاتها في عدد متزايد من برامج معالجة النصوص، ومحرّرات اللغات البرمجيّة وغيرها. نتناول في هذا الدرس شرح أساسيات التعامل مع هذه التعابير دون تخصيص الحديث عن برنامج ما، حيث سنستخدم الأداة egrep في أمثلتنا (والتي تستخدم للبحث ضمن الملفات النصيّة أو دخل المستخدم عن طريق التعابير النمطيّة وطباعة الخرج المطابق لها على شاشة الطرفية). التعابير النمطيّة تدعم التعابير النمطيّة نوعين من المحارف: الأول هو الأحرف الأبجديّة المعروفة، والآخر هو الرموز الخاصة Metacharacters، وهي ما تعطي التعابير النمطية فاعليتها الحقيقية. لنأخذ الملف التالي country.txt كمثال، وهو - كما نرى - مؤلف من سبعة أسطر وثلاثة أعمدة، العمود الأول يحمل اسم البلد، والثاني عدد سكانه، والأخير القارّة التي يقع فيها: $ cat country.txt India,1014003817,Asia Italy,57634327,Europe Yemen,1184300,Asia Argentina,36955182,Latin America Brazil,172860370,Latin America Cameroon,15421937,Africa Japan,126549976,Asia محارف الإرساء الخاصة لنبدأ في شرح المحارف الخاصة مع العلامتين ^ و $، واللتان تشيران إلى أول السطر وآخره على التتالي، وتسمى بمحارف الإرساء anchor metacharacters. فلو رغبنا مثلًا بمعرفة الأسطر التي تبدأ بحرف "I"، فإننا سنستخدم التعبير: $ egrep '^I' country.txt India,1014003817,Asia Italy,57634327,Europe وبالمثل، لتحديد الأسطر التي تنتهي بحرف "e"، نكتب: $ egrep 'e$' country.txt Italy,57634327,Europe العلامة التالية هي النقطة (.)، والتي تشير إلى محرف واحد (حرف، رقم، أو علامة)، فللبحث عن أسماء المدن المؤّلفة من خمسة محارف نطبع الأمر: $ egrep '^.....,' country.txt India,1014003817,Asia Italy,57634327,Europe Yemen,1184300,Asia Japan,126549976,Asia الآن لنجرّب البحث عن الأسطر التي تبدأ بحرف "I" أو "J" ومؤلفة من خمسة محارف: $ egrep '^[IJ]....,' country.txt India,1014003817,Asia Italy,57634327,Europe Japan,126549976 تسمّى الأقواس المستطيلة [] هنا بصفّ المحرف character class، وهي تبحث عن تطابق واحد فقط من المحارف التي تضمها مع النصّ. وإذا وضعنا بداخلها العلامة ^ فإنها تصبح صفّ استبعاد، أي تطابق كل النصّ المذكور عدا ما يلحقها، فلو أردنا البحث عن أسماء البلدان المؤلفة من خمسة محارف والتي لا يبدأ اسمها بحرف "J" ولا "I" فإننا نكتب: $ egrep '^[^IJ]....,' country.txt Yemen,1184300,Asia مجموعات المحارف الخاصة وتنويعاتها لمطابقة جميع الأسطر التي تضم كلمة Asia أو Africa نكتب: $ egrep 'Asia|Africa' country.txt India,1014003817,Asia Yemen,1184300,Asia Cameroon,15421937,Africa Japan,126549976,Asia كما يمكن إجراء ذات البحث باستخدام تعبير نمطي يستخرج حرفي A و a كعوامل مشتركة في الكلمتين: $ egrep 'A(si|fric)a' country.txt India,1014003817,Asia Yemen,1184300,Asia Cameroon,15421937,Africa Japan,126549976,Asia تحديد الكميّة بدلًا من كتابة العبارة: $ egrep '^[IJ]....,' country.txt يمكننا اختصارها بالشكل: $ egrep '^[IJ].{4},' country.txt يسمى القوسين المزهّرين هنا {} بمحدّدي الكمية، وتضم رقم يعبّر عن عدد المرات التي يجب أن يتكرر فيها المحرف قبل مطابقته، كما تُستخدم للتعبير عن مدى (مجال) من المرات: $ egrep '^[IJ].{4,6},' country.txt India,1014003817,Asia Italy,57634327,Europe Japan,126549976,Asia يبحث التعبير النمطي السابق عن أسماء البلدان التي تبدأ بالحرف "I" أو "J" وتتراوح عدد محارفها من 4 إلى 6. هناك أيضًا بعض الاختصارات التي يمكن استخدامها مع تحديد الكمية مثلًا المجال {0,1} والذي يعني "يوجد مرة واحدة على الأقل أو لا يوجد تمامًا"، يُكافئ بالرمز ؟، حيث يمكننا كتابة: $ egrep '^ab{0,1}c$' filename أو: $ egrep '^ab?c$' filename أيضًا المجال {0,} يُكافئ بالرمز *، والتي تعني عدد لا نهائي من المرات، حيث التعبير: $ egrep '^ab{0,}c$' filename يساوي بالنتيجة: $ egrep '^ab*c$' filename وكذلك المجال {1,} والذي يحدّد الكمية "مرّة واحدة على الأقل"، يُكافئ بالرمز +، ويكون التعبيرين التاليين متكافئين: $ egrep '^ab{1,}c$' filename $ egrep '^ab+c$' filename لنأخذ الآن بعض الأمثلة الأكثر تعقيدًا ولندمج ما تعلمناه من تعابير، لكن عوضًا عن البحث ضمن ملف نصيّ txt سنعالج دخل قياسي من قبل المستخدم. لنبحث مثلًا عن كل الاحتمالات الممكنة في تهجئة الجملة التالية: the grey colour suit was his favourite $ egrep 'the gr[ea]y colou?r suit was his favou?rite' the grey color suit was his favourite the grey color suit was his favourite the gray colour suit was his favorite the gray colour suit was his favorite لو نظرنا إلى التعبير المستخدم في هذا المثال، فإننا سنرى: الكلمة "grey" يمكن أن تلفظ grey أو gray. الكلمة "colour" تكتب بطريقتين: colour أو color، وهذا يعني بأن حرف (u) اختياري، لذلك استخدمنا العلامة ؟ والتي تعني "يوجد مرة واحدة على الأقل أو لا يوجد تمامًا. + كذلك الأمر مع الكلمة "favourite" حيث كتابة حرف (u) اختيارية لذا استخدمنا ذات العلامة ؟ لنجرّب الآن مطابقة عنوان الرمز البريدي zip code في الولايات المتحدة: $ egrep '^[0-9]{5}(-[0-9]{4})?$' 83456 83456 83456- 834562 92456-1234 92456-1234 10344-2342-345 مثال آخر يطابق جميع الأوقات الممكنة في الأربع والعشرين ساعة: $ egrep '^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]' 23:44:02 23:44:02 33:45:11 15:45:33 15:45:33 في المثال السابق قلنا أنه إذا كانت الخانة الأولى من الساعة إما 0 أو 1، عندها يسمح للخانة الثانية بأن تأخذ قيمة من المجال من 0 إلى 9، ولكن إذا كانت الخانة الأولى تحمل الرقم 2 حينها يسمح للخانة الثانية أن تأخذ القيم 0، 1، 2، أو 3. حدود الكلمة لكتابة تعبير نمطي يطابق الكلمات التي تنتهي بـ "color"، سنرى أنه يطابق الأمثلة: unicolor ،watercolor ،multicolor، لكنه لن يطابق colorless أو colorful: تطابق العلامة "<\" ورود كلمة color آخر الكلمة مثل unicolor، watercolor، أو multicolor، $ egrep 'color\>' أما لمطابقة كلمة color في أوّل الكلمة، مثل colorless أو colorful، فإننا نكتب: $ egrep '\<color' ولمطابقة كلمة color كما هي: $ egrep '\<color\>' الإحالات المرجعية لنفترض أننا نريد مطابقة جميع الكلمات المكرّرة مثل "the the" أو "before before"، هنا نستخدم ما يسمى بالإحالات المرجعية backreferences والتي تستعمل لتذكّر الأنماط، مثال: $ egrep "\<the\> \1" أو في الحالة العامّة: $ egrep "\<(.*)\> \1" يطابق التعبير السابق جميع الكلمات عندما تتشابه الثانية مع الأولى، كما يمكن استخدام زوج إضافي من الأقواس مع المرجع 2\ لمطابقة الكلمات فقط إذ تكررت أربع مرات، وهكذا.. ترجمة -وبتصرّف- للمقال: An Introduction To Regular Expressions للكاتب: Shantanu Kulkarni.1 نقطة
-
يُعتبر خادوما الويب Apache و Nginx الأكثر شهرةً من بين الخواديم المفتوحة المصدر في عالم الشبكة العنكبوتيّة، على اعتبار أنّهما مسؤولان عن خدمة وتأمين نصف تدفّق البيانات على الإنترنت، وعلى مَقدرة على تولّي مُختلف حجوم الأحمال، والعمل مع برمجيات أخرى في سبيل توفير حزمة من خدمات الويب الشاملة والكاملة لتلبية مُختلف الاحتياجات. بينما يَتشارك الخادومان Apache و Ngnix العديد من الميّزات والخاصيّات، فلا يُمكن اعتبارهما مُتشابهان، أو التفكير بأن أحدهما هو بديل عن الآخر، فكل منهما يتفّوق عن الآخر بشيءٍ ما، وعلى مُدير النظام فهم وإدراك لماذا يجب اختيار أحدهما دون الآخر، فهذا الدليل يَهدف إلى مُناقشة كيف أنّ كُل خادوم يَتميّز ويَبرز في شيء ويُخفق في آخر. مقدّمة عامّةسيتمّ أخذ نظرة سريعة عن تاريخ وبداية المشروعين وخواصّهم العامّة قبل التعمّق في الاختلاف بينهما. تاريخ خادوم الويب Apacheأَنْشَأَ “روبت ماك كول” (Robert McCool) “خادم الـ HTTP أباتشي” (Apache HTTP Server) في عام 1995، وتمّ مُتابعة تطويره تحت مظلّة “مُنشأةُ برمجيات أباتشي” (Apache Software Foundation) مُنذ العام 1999، وكان هذا الخادم هو المشروع الأساسي للمُنشأة والأكثر شهرةً عن باقي البرمجيات، فأصبح ببساطة يُشار إليه بالاسم “Apache”. يُعتبر خادم الويب أباتشي الخادم الأكثر استخدامًا على الإنترنت منذ العام 1996، وبسبب هذه الشهرة، استفاد أباتشي من توثيق ودعم باقي مشاريع البرمجيات الأُخرى. يَختار مُدراء الأنظمة الخادم أباتشي غالبًا بسبب مرونته، وقُدرته على التحمّل، وتوفّر دعمه العالي والمُنتشر، كما يُحسب له قابليته على التوسّع عبر نظام الوحدات (modules) الديناميكيّة، واستطاعته على مُعالجة عدد كبير من اللغات المُفسّرة (interpreted languages) من دون الحاجة إلى برمجيّة مٌستقلّة وسيطة. تاريخ خادوم الويب Nginxبَدأ “ايجور سيسيوﭫ” في عام 2002 العمل Nginx للإجابة وحلّ المُشكلة المعروفة بالاسم C10K، والّتي كانت تُشكّل تحدّيًا كبيرًا لخوادم الويب لتُصبح قادرة على تولّي عشرة آلاف اتصال مُتزامن (concurrent) وذلك في تلبية حاجة الويب الحديث، وأُنتجت الإصدارة الأوليّة والمُتاحة للعُموم في عام 2004 مُقدمةً حلًا لهذه المُشكلة، وذلك بالاعتماد على معماريّة مدفوعة بالحالة (event-driven) ولا مُتزامنة. انتشر Nginx كالنار في الهشيم مُنذ إصداره، بمُقتضى خفته واستخدامه الخفيف للمَوارد، وقدرته على التوسّع وتحمّل الضغط العالي وبأقل عتاد مُمكن، وتفوّق Nginx بتأمين المُحتوى الثّابت (static content) بسرعة، وبتصميمه لتمرير الطلبات الديناميكيّة (المُتغيّرة) إلى برمجيات أُخرى قادرة على مُعالجة هذا النوع من المُحتوى. يختار مُدراء الأنظمة الخادم Nginx غالبًا بسبب استهلاكه الأمثل للمَوارد، واستجابته العالية مع الضغط المتزايد. معماريّة مُعالجة الاتصال (Connection Handling Architecture)يَكمن الاختلاف الواضح بين أباتشي و Nginx في طريقة كلٍ منهما في مُعالجة الاتصالات وتدفّق البيانات (traffic)، وربّما هذا السبب وراء الاختلاف في طريقة كل من الخادمين في الاستجابة إلى حالة تدفّق البيانات المُختلفة. وحدات أباتشي (Apache Modules)يقدّم أباتشي تشكيلةً من وحدات المُعالجة المُتعدّدة (multi-processing)، والّتي يُطلق عليها بـ MPMs، والغرض منها تحديد آليّة مُعالجة طلبات العميل، ويَسمح ذلك مُدراء الأنظمة عامّةً بالتبديل بين معماريّة مُعالجة الاتصال بسهولة، والوحدات هي: mpm_prefork: تَستنسخ وتُوالد هذه الوحدة الخاصّة بالمُعالجة عمليّات (processes) باستخدام سلسلة وحيدة (single thread)، على أنّ تكون كل عمليّة لمُعالجة طلب، ويستطيع كل ابن مُعالجة اتصال واحد في نفس الوقت، وطالما أنّ عدد الطلبات أقل من عدد العمليّات، فستكون هذه الوحدة سريعةً جدًا، ولكن يَنخفض الأداء بسرعة بعد تخطّي الطلبات عدد العمليّات، ولذلك لا يُعتبر هذا النمط بالخيار الجيّد للعديد من السيناريوهات، فكل عمليّة لها تأثير بليغ على استهلاك الذاكرة (RAM)، ولهذا السبب تُصعّب هذه الوحدة من عمليّة التوسّع بطريقة مُلائمة، ومع هذا تبقى هذه الوحدة خيارًا جيّدًا إن تمّ استخدامها مقترنةً مع مُقوّمات (components) أُخرى لم تُبنى بالأساس آخذةً بعين الاعتبار السلاسل (threads)، فلغة PHP ليست سلسلة آمنة (thread-safe)، ولذلك يُنصح بهذه الوحدة باعتبارها الطريقة الوحيدة الآمنة للعمل مع mod_php والّتي هي وحدة من وحدات أباتشي لمُعالجة هذا النوع من الملفّات. mpm_worker: تستنسخ وتُوالد هذه الوحدة عمليّات (processes) كل منها ذات قُدرة على إدارة سلاسل مُتعدّدة (multiple threads)، تستطيع كل واحدة من هذه السلاسل مُعالجة اتصال وحيد، تُعتبر هذه السلاسل أكثر كفاءة من العمليّات، بسبب أنّ هذه الوحدة تتوسّع (scales)، وتتحمّل المزيد من الضغط أفضل من الوحدة prefork MPM، وباعتبار أنّه يوجد سلاسل أكثر من العمليّات، فهذا يعني أيضًا أنّ الاتصالات الجديدة تستطيع مُباشرةً استهلاك واستخدام سلسلةبدلًا من اضطرارها إلى الانتظار لتوفّر عمليّة مُتاحة. mpm_event: تَعمل هذه الوحدة بشكل مُشابه إلى الوحدة worker، ولكنها مُحسّنة لتتولّى اتصالات keep-alive، فعند استخدام الوحدة worker فإن الاتصال سيَحتفظ بالسلسلة (thread) بصرف النظر فيما إذا كان الطلب نشطًا فيما صُنع من أجله ما دام الاتصال محفوظًا نشطًا، فالوحدة mpm_event تتولّى الاتصالات keep-alive بالاحتفاظ جانبًا بسلاسل مكرّسة لمُعالجتها، وبتمرير الطلبات النشطة إلى سلاسل أُخرى، وهذا من شأنه أنّ يُبعد الوحدة من الغوص بطلبات keep-alive، الأمر الّذي يُجيز بتنفيذ الطلبات (execution) بشكل أسرع، وهذا الأسلوب أصبح يعمل بشكل مُستقر مع الإصدار Apache 2.4. أصبح الأمر أكثر وضوحًا، حيث تقدّم خوادم أباتشي بنية معماريّة مرنة للاختيار بين مُختلف الاتصالات وخوارزمية مُعيّنة في مُعالجة الطلبات، وبُنيت هذه الخيارات في الدرجة الأولى من تطوّر الخادم والحاجة المُتزايدة للاتصالات المُتزامنة لتواكب طبيعة الإنترنت بعد تغيرها وتطوّرها. آليّة مُعالجة الاتصال في خادوم Nginxقَدِم Nginx إلى عالم خوادم الويب بعد قدوم أباتشي، مَبنيًّا ومُعدًّا لمشاكل الاتصالات المُتزامنة (concurrency) الّتي تواجه المواقع عند التوسّع، مُحمّلًا بهذه المعرفة، صُمّمَ Nginx من جذوره ليستخدم خوارزميّة في مُعالجة اتصالات مدفوعة بالحالة (event-driven)، غير مُستوقفة (non-blocking)، ولا مُتزامِنة. يستنسخ ويُوالد Nginx من العمليّات العاملة (worker processes)، ويستطيع كلٍ مِنها مُعالجة آلاف الاتصالات، وتُنجذ العمليّات العاملة ذلك عن طريق تطبيق/تنفيذ آلية حلقيّة سريعة، والّتي تفحص بشكل مُستمر حالات العمليّات (processes events)، وإن فصل المُهمّة الفعليّة عن الاتصالات يسمح لكلعامل بالاهتمام بنفسه باتصال فقط عند بدء حالة جديدة. يتموضع كل اتصال مُعالج من قبل العامل ضمن حلقة الحالة (event loop) في مكان تواجد بقية الاتصالات، وتُعالج الأحداث بشكل لا مُتزامن ضمن الحلقة، ليتمّ إنجاز المُهمّة بطريقة غير مُستوقفة (non-blocking)، وعندما يُغلق الاتصال سيتمّ نزعه من الحلقة. يَسمح هذا الأسلوب من مُعالجة الاتصال Nginx بتحمّل الضغط العالي بشكل لا يُصدّق وبأقل المَوارد المُتاحة، وباعتبار أنّ الخادم ذو سلسلة وحيدة (single-threaded) ولا تتوالد العمليّات لتُعالج كل اتصال جديد، فإن استهلاك المُعالج (CPU)، والذّاكرة يبقى ثابتًا نسبيّا، حتّى في أوقات الذروة. كيف يُعالج أباتشي و Nginx المُحتوى الثّابت والمحتوى الديناميكي (المُتغيّر)إن أبرز ما يتمّ النظر إليه عند المُقارنة بين الخادمين هي طريقة كلٍ منهما في التعامل مع طلبات المُحتوى الثّابت (static)، والمُحتوى المُتغيّر (dynamic). كيف يتعامل أباتشي مع المُحتوى الثّابت والمُتغيّرتستطيع خوادم أباتشي التعامل مع المُحتوى الثّابت باستخدام الطريقة التقليديّة المُعتمدة على الملفّات، ويعود أداء هذه الإجراءات (operations) في الدرجة الأولى على دور ووظيفة أساليب الوحدات (MPM) المشروحة سابقًا. يستطيع أباتشي أيضًا مُعالجة المُحتوى الديناميكي (المُتغيّر) بدمج مُعالج اللغة المُراد مُعالجتها داخل كل من نماذجه العاملة (worker instances)، وهذا يَسمح له بتنفيذ المُحتوى الديناميكي داخل خادم الويب نفسه بدون الحاجة إلى الاعتماد على مُقوّمات خارجيّة، ومن المُمكن تفعيل هذه المُعالجات الديناميكيّة (المُتغيّرة) من خلال استخدام وحدات قابلة للتحميل بشكل ديناميكي. إن قدرة أباتشي على مُعالجة المُحتوى الديناميكي بشكل داخلي تعني أنّ الإعداد يُصبح أسهل لمُعالجة هذا النوع من المُحتوى، فليس من الضروري تنسيق عمليّة الربط مع برمجيات إضافيّة، وتستطيع الوحدات وبسهولة أنّ تقوم بالتبديل عندما تتغيّر مُتطلّبات المُحتوى. كيف يتعامل Nginx مع المُحتوى الثّابت والمُتغيّرلا يَملك Nginx بطبيعته أي قدرة على مُعالجة المُحتوى الديناميكي، ولمُعالجة شيفرة PHP وطلبات المُحتوى الديناميكي، فإن على Nginx تمرير الطلبات إلى مُعالج خارجي من أجل التنفيذ (execution) والانتظار ريثما يتم الانتهاء من مُعالجة هذا المُحتوى ليتمّ إعادة إعادته، ومن ثم عرض النتائج على العميل. يَنبغي على مُدراء الأنظمة الانتباه إلى أنّ الأسلوب الّذي ينتهجه Nginx يستوجب إعدادًا بينه وبين المُعالج وباستخدام واحدًا من البرتوكولات الّتي يفهمها Nginx أمثال: HTTP, FastCGI, SCGI,uWSGI, memcache، وهذا من شأنه أنّ يُعقّد الأمور بعض الشيء، خصوصًا عند مُحاولة توقّع عدد الاتصالات اللازم السماح بها، حيثُ أنّه سيتمّ استخدام اتصالًا إضافيًا لكل مُعالج يتمّ استدعاؤه. من ناحية أخرى، إن هذا الأسلوب يقدّم بعضًا من الأفضليّة، عندما نعلم أنّ مُفسّر المُحتوى الديناميكي غير مُدمج في عمليّة العامل، وتكلفة هذه الطريقة ستُدفع للمُحتوى الديناميكي فقط، وعلى أنّ يُقدّم المُحتوى الثّابت بطريقة مُباشرة، ولا يتمّ الاتصال بالمُفسر إلا عند الحاجة، والجدير بالذكر أنّ أباتشي يستطيع العمل بهذا الأسلوب، ولكن بعمله ذلك سيتخلّى عندها عن بعض ميزاته. الاختلاف بين الإعداد الموزّع (Distributed) والإعداد المركزي (Centralized)يَعتبر مُدراء الأنظمة الاختلاف الأكبر والبارز بين هذين الخادمين هو فيما إذا كان الإعداد والتخصيص على مُستوى المسار مسموحًا أو لا ضمن مسارات (directories)المُحتوى. فلسفة Apache في الإعداديُضمّن أباتشي خيارًا للسماح بالإعداد لكل مسار عن طريق تَفحُّص (inspecting) وتفسير (interpreting) التعليمات أو التوجيهات الموجودة في الملفات المخفيّة داخل مسارات المُحتوى نفسها، وهذه الملفّات معروفة بملفات .htaccess. باعتبار أنّ هذه الملفّات تَقطن داخل مسارات المُحتوى نفسها، فعند مُعالجة طلبٍ ما، فإن أباتشي يَفحص كل جزء من مسار الملفّ المطلوب باحثًا عن ملفّ .htaccess ليُطبّق التوجيهات الّتي بداخله، وهذا من شأنه أنّ يسمح للإعداد اللامركزي لخادم الويب، والذي غالبًا ما يُستخدم لإنجاز: إعادة كتابة عنوان الموقع (URL rewrites)تقييد الوصول (access restrictions)التفويض والمُصادقة (authorization and authentication)سياسات التخبئة (caching policies)بالطبع يُمكن للأمثلة السابقة إعدادها عن طريق ملفّ إعدادات أباتشي الرئيسي، ولكن استخدام ملفات.htaccess يَملك بعض الميزات: أوّلًا، باعتبار أنّها تُفسّر في كل مرّة توجد بها مع المسار المطلوب، فهي تُنفّذ مُباشرةً بدون إعادة تحميل الخادم.ثانيًا، تجعل من المُمكن السماح للمُستخدِمين غير المصرّح لهم بالتحكم بجانب معيّن من المُحتوى الخاصّة بهم بدون إعطائهم تحكم كامل لملفّ الإعدادات.يُقدّم هذا النمط من الإعداد طريقة سهلة ونموذجيّة، وخاصّة لبعض برمجيات الويب، مثل أنظمة إدارة المُحتوى (CMS)، لغرض إعداد بيئتها بدون مَنح إذن وصول إلى ملفّ إعدادات مركزيّ، وكما هو معروف يُستخدم هذا الأسلوب مع مزودات الاستضافة المُشتركة (shared hosting providers) لصون والحفاظ على الإعدادات الرئيسية مع إمكانيّة منح العُملاء أفضليّة التحكّم بمسارات مُعيّنة. فلسفة Nginx في الإعدادلا يُفسّر Nginx ملفّات .htaccess، ولا يُقدّم أي آليّة للتعامل مع كل مسار من دون استخدام ملفّ إعدادات رئيسي، قد يبدو هذا الأسلوب أقل مرونةً من أسلوب أباتشي، ولكن من ناحية أُخرى فلهذا الأسلوب أفضليته. إن عدم الاعتماد على نظام ملفّات .htaccess الخاصّ بالإعداد على مستوى المسارات يُقدّم سرعةً في الأداءً، فخادم أباتشي عليه القيام بتفحّص المسار الجذر لكل طلب يصله، وعند وجود ملفّ أو أكثر خلال عمليّة البحث، يتم قراءة محتوياته وتفسيرها، ولكن Nginx لا يفعل ذلك، فهو يخدم الطلبات بسرعة أكبر بالاعتماد على مكان وحيد للبحث عنه. أفضليّة أُخرى مُتعلّقة بالحماية، فإن توزيع ملفّات الإعدادات على مستوى المسارات يوزّع مسؤوليّة الحماية على كل المُستخدِمين، الّذين قد يكونوا في معظم الأحوال غير موثوقون لتولّي هذه المُهمّة بالشكل الأمثل، فعندما يقوم مُدير النظام بتولّي مُهمة التحكم بالخادم ككل، يمنع ذلك من حدوث أخطاء، والتي قد تحدث في حال منح صلاحيات وصول لأطراف أُخرى. يجدر الذكر أنّه من المُمكن تعطيل تفسير ملفّات .htaccess’ فيأباتشي`، في حال أنها تُشكل نوعًا من القلق لمُدير النّظام. الاختلاف بين تفسير الملفّات وتفسير URIلا يقتصر الاختلاف بين الخادومين على ما سبق فقط، فيظهر الاختلاف الآخر في كيفيّة تفسير كلٍ منهما للطلبات (requests) وربطها مع المَوارد المُتواجدة على النّظام. كيف يُفسر Apache الطلباتيقدّم أباتشي القدرة على تفسير الطلب إما كمَورد فيزيائي (حقيقي) على نظام الملفّات (filesystem) أو عنوان URI الّذي قد يحتاج أسلوب أكثر تجرّد، ويستخدم أباتشي بشكلٍ عام للأسلوب الأول كتل (blocks) وهي إما <Directory> أو <Files>، بينما يستخدم كتل <Location>للمَوارد الأكثر تجرّدًا. صُمّم أباتشي من الأساس كخادم ويب، لذلك في مُعظم الأحيان فإن السلوك الافتراضيّ هو تفسير الطلبات كمَوارد نظام ملفّات (filesystem)، حيثُ يبدأ بتتبّع جذر المُستند وإلحاقه بجزئية الطلب متبوعًا باسم المُضيف (host) ورقم المنفذ في مُحاولة لإيجاد الملفّ المطلوب، بمعنى آخر وببساطة، تتمثّل هرميّة نظام الملفّات على الويب كشجرة مُستند. يُقدّم أباتشي عددًا من البدائل عندما لا يتوافق الطلب مع نظام الملفّات المقصود، فمثلًا من المُمكن استخدام الموجّه Alias لربط عنوان البديل، مع العلم أنّ استخدام الكتل <Location> هو طريقة للتعامل مع URI نفسها بدلًا من نظام الملفّات، كما يُمكن استخدام التعابير النمطيّة (regular expression)، والّتي من المُمكن استخدامها لتطبيق الإعداد بسهولة أكبر في كامل نظام الملفّات. يُعوّل أباتشي على نظام الملفّات بشكل كبير، يظهر ذلك جليًا في اعتماده على ملفّات .htaccessلإعداد كل مسار، حتّى أنّ التوثيق الرسميّ الخاصّ به يحذر من استخدام كتل مُعتمدة على URI في تقييد الوصول عندما يكون الطلب يَعتمد بشكل أو بآخر على نظام الملفّات. كيف يُفسّر Nginx الطلباتأُنشِأ Nginx ليكون خادمًا للويب وخادمًا وكيلًا/وسيطًا (proxy server)، ونظرًا إلى المعماريّة المطلوبة للعمل بهذه الأدوار، فإنه يعمل بشكل رئيسي مع URIs، والتحويل إلى نظام الملفّات عند الضرورة. يُمكن رؤية ذلك في بعض جوانب بناء وتفسير ملفّات إعدادات Nginx، فلا يوفّر Nginx آليّة لتحديد إعدادًا لمسار نظام الملفّات، بل يَستعيض عنها بتحليل URI نفسه. يُمكن توضيح ذلك بالمثال، كتل الإعداد الأوليّ لـ Nginx هي: server و location، الكتلة serverتُفسّر المُضيف الجاري طلبه، بينما الكتل مسؤولة عن مُطابقة أجزاء من URI التي تأتي بعد المُضيف والمنفذ، في هذه المرحلة يتمّ تفسير الطلب كـ URI، وليس كعنوان على نظام الملفّات. يتوجّب في نهاية المطاف على جميع الطلبات أنّ تُربط مع عنوان على نظام الملفّات وذلك للملفّات الثّابتة، فأولًا، سيَختار Nginx كتل server و location الّتي سوف تتولّى الطلب، ومن ثم ضم جزر المُستند مع الـ URI. إن تحليل ومُعالجة الطلب قبل كل شيء على شكل URIs بدلًا من عناوين نظام الملفّات يَسمح لـ Nginxالعمل بسهولة وعلى حدٍّ سواء كخادم وسيط (بروكسي)، وكخادم بريد، وخادم ويب، فقد تمّ إعداده ليُلائم كيف له أنّ يستجيب لأنماط الطلبات المُختلفة، ولا يفحص Nginx نظام الملفّات حتّى يكون جاهزًا ليَخدم الطلب، وهذا يُفسّر لماذا هو لا يُطبّق نمط ملفّات .htaccess. الوحداتيتوسع كلا الخادومان عن طريق نظام الوحدات، ولكن طريقة عملهما تختلف عن الآخر بشكل كبير. كيف يستخدم أباتشي نظام الوحدات (Modules)؟يَسمح نظام وحدات أباتشي بطريقة آليّة وديناميكيّة بتركيب ونزع الوحدات ليتناسب مع مُتطلّبات مُدير النظام خلال عمليّة تشغيل الخادم، وتتواجد نواة أباتشي دائمًا بكل جاهزية، بينما يُمكن تشغيل أو تعطيل الوحدات، أو حتّى حذفها أو إضافة ما يلزم إلى الخادم. يستخدم أباتشي الوحدات في العديد من المهام، ونظرًا للباع الطويل لمنصة أباتشي، فيتوفّر عدد هائل من مكتبات الوحدات، والّتي من المُمكن استخدامها في تعديل بعض الوظائف الداخليّة في بنية خادم أباتشي، فمثلًا الوحدة mod_php تقوم بدمج مُفسّر PHP داخل كل عامل (worker). لا تنحصر الوحدات لمُعالجة المُحتوى الديناميكي (المُتغيّر) فقط، فيُمكن استخدامها في العديد من الوظائف، فيُمكن استخدامها في: rewriting URLs: إعادة كتابة العناوينauthenticating: المُصادقةlogging: التّتبّعcaching: التخبئةcompression: الضغطproxying: الوساطةencrypting: التشفيركيف يتعامل Nginx مع نظام الوحدات (Modules)يُطبّق ويتعامل Nginx مع نظام الوحدات، ولكن يختلف الأمر عما هو في نظام أباتشي، فلا تُحمّل الوحدات بشكل ديناميكي في نظام Nginx، لذلك يجب على هذه الوحدات أنّ تُختار وتُترجم (compiled) إلى النواة. قد يبدو الأمر صعبًا للعديد من المُستخدمين وخاصّة لهؤلاء الذين لا يُحبذون صيانة برمجياتهم المُترجمة (compiled) بدون الاستعانة بنظام حزم تقليدي، وعلى الرغم من أنّ حزم الموزّع تتضمّن الوحدات الأكثر استخدامًا، ولكن عند الحاجة إلى وحدة غير شائعة، سيتوجب على مُدير النظام بناء الخادم من المصدر بنفسه. تَبقى وحدات Nginx مع ذلك ذات فائدة، وتسمح لمُدير النّظام بتحديد ماذا يجب على الخادم أنّ يحتوي من وظائف، أيضًا بعض المُدراء ينظر إلى الأمر من منظور الحماية، حيثُ أنّ المُكوّنات الاعتباطيّة لا يُمكن أن تُدرج داخل الخادم. تُقدّم وحدات Nginx مقدرات مُماثلة للوحدات الّتي المُقدّمة من أباتشي، على سبيل المثال، توفّر وحدات Nginx: proxying support: الوساطةcompression: الضغطlogging: التّتبّعrewriting: إعادة كتابة العنوانauthentication: المُصادقةencryption: التشفيرالدعم والتوافُقيّة والتوثيقيجب دائمًا التأكد من آليّة بناء الخادم ومُتطلباتها، وما الّذي على مُدير النّظام عمله لبناء خادم يعمل بأبسط الإمكانيات، وما هو حجم الدعم والمُساعدة المتوفّر لهذه البرمجية. الدعم في أباتشييُعرف الخادم أباتشي بباعه الطويل في هذا المجال، ولذلك فإن الدعم الخاصّ به متواجد وبقوّة، حيثُ يتوفّر توثيق مُمتاز لمكتباته الخاصّة به ومكتبات الطرف الثالث (الخارجيّة)، وعلى كافّة المُستويات، سواء كان لنواة الخادم أو للجزئيات المُرتبطة ببرمجيات أُخرى. يتوفّر بجانب التوثيق، العديد من الأدوات ومشاريع الويب لتسريع بدء العمل مع بيئة الخادم، وهي موجودة ضمن المشاريع نفسها أو مُتوفّرة ضمن برمجيات الحزم المعروفة. يَملك أباتشي بشكلٍ عام دعمًا قويًا من قِبل مشاريع الطرف الثالث، وذلك بسبب حصته السوقيّة، وقِدَمه، كما يَملك مُعظم مُدراء الأنظمة بشكل أو بآخر معرفة جيّدة بخادم أباتشي، ليس فقط بسبب انتشاره، ولكن أيضًا بسبب أنّ معظمهم بشكل أو بآخر يستخدم الاستضافة المُشتركة (shared-hosting)، والّتي تعتمد على خادم أباتشي بشكل حصري، لمقدرته الإدارية الموزّعة باستخدام ملفّ .htaccess. الدعم في Nginxيكسب Nginx المزيد من الدعم مع ازدياد المُستخدِمين بسبب أدائه العالي، ولكن يبقى عليه التطوير من نفسه في بعض الجزئيات. قد كان من الصعب إيجاد توثيق مفهوم وواضح بالغة الإنكليزية للخادم Nginx في البداية، نظرًا إلى أنّ تطويره وتوثيقه تمّ بالغة الروسية، ومع ازدياد الاهتمام بالمشروع، أصبح هناك وفرة من المصادر على الموقع الرسميّ وغيره من الدعم الخارجي. أصبح الدعم متوفّرًا أكثر من ذي قبل فيما يخص تطبيقات الطرف الثالث، وبدأت بعض الحزم بتقدم خيارات الإعداد التلقائي سواءً لـ أباتشي أو Nginx، وعند عدم توفّر الدعم، فإن إعداد Nginx مع البرمجيات البديلة عادةً ما يكون واضحًا ومُيسرًا طالما أنّ برمجية الطرف الثالث تملك توثيقًا جيّدًا لمُتطلّباتها. استخدام Apache و Nginx معًاتمّ عرض فوائد وقصور كلا الخادومين، ويجب على مُدير النّظام في هذه المرحلة أنّ يُحدّد أيًا منهما يُناسب احتياجاته، ولكن يجد العديد من المُستخدِمين أنّه من المُمكن تقوية خادوم الويب عند استخدام كلٍ من أباتشي و Nginx جنبًا إلى جنب. إن إتمام هذه الشراكة يتمّ عن طريق تَمَوْضُع Nginx أمام أباتشي كوكيل/وسيط عكسي (reverse proxy)، وهذه يسمح لـ Nginx بمُعالجة جميع الطلبات من العُملاء، الأمر الّذي يَسمح بالاستفادة من سرعة Nginx وقدرته على تولّي عدد كبير من الاتصال في وقتٍ واحد. إن خدمة الملفّات الثّابتة بسرعة كبيرة ومباشرةً إلى العُملاء، هو الأمر الّذي يتفوّق به Nginx، ولكن وللمُحتوى الديناميكي، ملفات PHP مثلًا، سيُوكل Nginx الطلبات إلى أباتشي لمُعالجتها والعودة بصفحة بالنتيجة النهائيّة، ليستطيع Nginx عندها تمرير المُحتوى إلى العميل. يعمل هذا الإعداد بشكل جيّد للعديد من مُدراء الأنظمة، وذلك بسبب أنّه يسمح لـ Nginx ليعمل كآلة فرز وتصنيف، بمعنى أنّه سيتولّى مُعالجة جميع الطلبات طالما أنّه يستطيع ذلك، وتمرير ما لا يستطيع التعامل معه، وبتخفيض الطلبات المطلوب من خادم أباتشي تولّيها، سيُخفف بعضًا من الاستيقاف (blocking) الّذي قد يحدث عندما يستهلك أباتشي المزيد من المُعالج. يَسمح هذا الإعداد أيضًا لمُدراء الأنظمة بالتوسّع عن طريق إضافة خادم خلفي (backend) على حسب الحاجة والضرورة، ومن المُمكن إعداد Nginx لتمرير الطلبات إلى تجمّع (pool) من الخوادم بسهولة، الأمر الّذي يزيد من الأداء والفعاليّة. الختاميُقدّم كلًا من أباتشي و Nginx مُرونة في الاستخدام، وقوّةً في الأداء، والتفضيل بينهما هو لأمرٌ يَعتمد على تقدير مُتطلبات ووظائف الخادم، وعلى مُدير النّظام أنّ لا يسأل: هو أفضل خادم ويب؟، بل السؤال الّذي يجب سؤاله هو، ما هو أفضل خادم ويب لمشروعي الّذي يتطلّب كذا وكذا؟ يوجد بالفعل اختلافات بين المشروعين، هذه الاختلافات من شأنها أن تأثر على الأداء، والقدرات، والوقت المُستغرق في إتمام أي منهما للعمل بالجاهزيّة الكاملة، ولكي يتمّ اختيار الأفضل يُنصح بعمل تسوية أو مقايضة بين المحاسن والمساوئ، ففي نهاية المطاف لا يوجد خادم يُلبي كافة الاحتياجات، ويَكمن الحلّ في ترتيب الأولويات. ترجمة –وبتصرّف– للمقال Apache vs Nginx: Practical Considerations لصاحبه Justin Ellingwood.1 نقطة
-
أحبّذ كثيرًا العمل كمُستقلّ فالعمل كُمُستقل يجلب لي سرورًا خاصًا في النفس، ولكن غالبًا هذا ليس حال جميع المُستقلين، فهم يعتقدون عكس ذلك، ويظنون أنّي صاحب عقليّة خاصّة. يسري في مجتمع الويب مُعتقدٌ مُعيّن، أنّ العمل كمُستقلّ وتحمل تبعاته وعقباته هو أمرٌ يفعله البعض فقط عند حاجتهم لتمويل المشاريع الّتي يهتمون بها، يعتبرني البعض بأن شخص مُنعزل وأغني على ليلاي، وذلك فقط لأني لا استهوي العمل مع الشركات الناشئة أو حتَّى العمل على مشروعي الخاصّ، فقد ارتبط العمل الحرّ بمظاهر التوتّر وعدم المرونة، خاصّة تلك المشاريع الّتي يكون أصحابها من النوع صعب التعامل وأصحاب طلبات متكرّرة، ولكنّي أنظر إلى الأمور من منظور آخر، فالعمل مع العُملاء والعمل الحرّ هو أمرٌ لطالما بعث في نفسي الحماس، فهو يجعل مني أقدّر المشروع الّذي أعمل عليه أكثر كما لو كان مشروعي، وليس بالضّرورة أنّ يكون الأمر بذلك التعقيد الذي يتحدّث عنه البعض، بل على العكس، قد يكون الأمر فرصة للدخول في تجربةٍ فريدةٍ تستحق العناء. هل هي عقليّة العميل؟ أرى يوميًا على صفحات التواصل الاجتماعيّ سواءً فيسبوك أو تويتر كيف يُبدي المُستقلّون شكواهم حول العُملاء، أو أنّهم على الأقل يشيرون إلى بعض الحالات الشائبة والتجارب الفاشلة، حيث ُيَميل مُعظم المُستقلّون إلى الترويح عن نفسهم والتحدث عن عوز العُملاء إلى إدراك حقيقة الأمور، بجانب ترددهم في مشاريعهم، وعدم رضاهم في معظم الأحيان. كنت أتحدث قبل مُدّة مع أحد المُستقلين عن العمل الحر وعن تجربتي الشخصيّة في هذا المجال، وأذكر أنّي قلت: لسبب ما أنا لم يحدث وسبق لي أنّ تعاملت مع عميل ذو تعامل سيّئ من النوع الّذي يتحدث عنه مُعظم المُستقلّون كنت أعتقد ولفترة طويلة أنّه الحظّ وفقطّ الحظّ هو السّبب في أنّ جميع عُملائي من أصحاب التفكير العمليّ والعقول الليّنة والمرنة، ولكن ما كان منّي أن أدركت أنها ليست الصُدفة والحظّ وراء السبب في تعاملي مع هذا النوع من العُملاء، بل ربما يكون السبب هو أسلوبي وعقليتي كمُستقلّ ذو منهجية هي السر في أن جميع عملائي من العُملاء الذي يتمناهم أي مُستقلّ. لا أنكر أنّ للحظّ دورًا ما في حصولي على هذا النّوع الطيّوب من العُملاء بطبيعة الحال، ولكن أريد أن أوضح نقطة هامّة، وهي أنّي عندما أقوم بالبحث عن مشاريع مُحتملة لي، فأنا أبحث عن المشاريع الّتي يَبدو على أصحابها الشغف حول مشاريعهم، وليس ذلك فقطّ بل من يَهتم بالجودة النهائيّة بشكل حقيقيّ وبشكل نابعٍ من القلب، وهذا النّوع من العُملاء وفقطّ هذا النّوع هو من سيجعل من المشروع مُيسّرًا من دون الدخول في متاهاتٍ لا يُحمَد عُقباها. غيّر العمل الحرّ من نظرتي المُسبقة حول العُملاء، حيثُ أنّي كنت أنظر نظرة استخفاف إلى بعضهم، بالضبّط أولئك الذين لا يُدركون حقيقة الأمور، وأصبحت أجد فيهم فرصةً لتثقيفهم ووضعهم على الخطوات الأولى نحو فهم الأمور بشكلها الصحيح، على عكس ما كنت أفعل سابقًا وهو الشكوى والتّذمّر، حيثُ أنّ هذا التغيير من شأنه أن يَنقل المُستقلّ من حالة عدم الثقة بالعميل إلى حالةٍ مُغايرةٍ تمامًا، وهي التعامل مع العميل بشفافيّة تامّة تُسهل من إنجاح المشروع. السر في التواصل بحكم طبيعة عمل المُصمّمين، فإن جلّ عملهم هو في التواصل مع العُملاء، ولذلك فإن المُصمّم الّذي يجد صعوبة في التواصل مع عملائه سيُعاني الأمرين مع العمل الحرّ، وإن كان يَعتقد المُصمّم أن عمله كمُستقلّ أو عمله عن بُعد (remotely) سيكون خاليًا من أي نوعٍ من أنواع الاتصال، فإن هذا المُستقلّ ليس فقطّ مُخطئ، بل هو يَطير ويُحلّق خارج السرب ويغرد بعيدًا عنه. يَجب على المُصمّم أنّ يكون قادرًا على وصف حقيقة الأمور وماهيتها، فإن القدرة على توصيل الأفكار بشكل شفهيّ أو كتابيّ، أو حتَّى بصري هو أمرٌ سيَمنع المُستقلّ من إنشاء نماذج (mockups) مُتلاحقة لكلٍ تفصيل صغير في المشروع. يجب على المُصمّم أنّ يكون قادرًا على تفسير وشرح تفصيلات تصميمه وتقديم الأسباب وراء اختيار جزئيّة مٌعيّنة دون غيرها، وذلك في سبيل تجنب طلبات التعديل المتكرّرة وغير النهائيّة، وبشكلٍ يجعل من المُستقلّ مجرّد أداة تخضع لذوق العُميل وآرائه الاعتباطيّة، ولأن المُصمّم هو الخبير، وصاحب التجارب السابقة، فمن واجبه تفسير وتوضيح الأمور للعميل. يجب على المُصمّم أنّ يكون قادرًا على التحكم بزمام الأمور، فهو صاحب النّظرة التصميميّة الأقوى، وألا ينتظر من العميل أن يَطرح قائمة من المهام لكي يتمّ تنفيذها، حيثُ أن هذا الأسلوب قد يدفع العُملاء إلى الإحباط، الأمر الذي قد لا يترك فرصة ثانية للمُستقلّ ليُثبت بها نفسه، مع العلم أن بعض العُملاء تتوقع من المُستقلّ استلام قيادة المشروع والتحكم به من الألف إلى الياء، وذلك في سبيل الوصول إلى بر الأمان، ولذلك على المُصمم أن يَعي أهميّة تحديد واختيار المُستوى المطلوب من المُتابعة والتفاعل مع العميل لقيادة المشروع إلى بر الأمان. يجب أن يَملك المُصمّم ثقةً بعمله وبتصميمه، حتَّى لو كان التصميم في مرحلته الأوليّة وغير مُدقّقٍ بشكل نهائيّ، حيث أن مشاركة التصميم الأوليّ مع العميل وقبل الموعد النهائيّ وبشكل متكرّر هو السبيل في الحصول على ردود فعلٍ إيجابية (feedback) ذات قيمة، وسيمنع ذلك من طلب العميل تعديلًا جذريًّا في نهاية المشروع، وتسمح للعميل بالتركيز على النقد البنّاء الّذي من شأنه أن يَسمح بدوران عجلة الإنتاجيّة نحو الأمام. لوم العميل يبدأ مُعظم المُستقلّين في عالم العمل الحرّ جاعلين نصب أعينهم عدم طرح شيءٍ من عملهم قد يَظهر عليه شيءٌ من النّقص أمام عُملائهم، بمعنى آخر يَجد المُستقلون أنفسهم مدفوعين إلى بذل أقصى ما لديهم لتقديم أعمالهم بشكلٍ يميل إلى المثاليّة، وفي حال حصولهم على استجابة تقلّ عن توقعاتهم، سيصابون بشيءٍ من الإحباط الأمر الّذي من شأنه أن يُؤثر على المُنتج النهائيّ، مما يدفعهم نحو الدفاع عن آرائهم ولوم العميل لعدم تزويدهم بالمعلومات الكافية والوافية للصورة النهائيّة للمشروع. مُساعدة العُملاء في جعلهم عُملاءً أفضل من النادر أن يُوظّف صاحب المشروع مُستقلًا وهو يَعلم باطن الأمور وخفاياها بنفسه، فالعُملاء ليس بالضّرورة أن تأتي حاملة معها المعرفة المطلوبة لتكون من النوع المرن في المُعاملة، فمن المُحتمل جدًا أن يكون العميل لم يسبق له أن عمل مع مُستقلٍ من قبل، ولذلك على المُستقلّ أن يكون صاحب المُبادرة في توجيه العميل نحو الطريق الصحيح. كيف يُطوّر المُستقلّ نفسه وصلّت إلى نقطةٍ من مسيرتي المهنيّة كمُصمّم ومُستقلّ مُحترف أدرك فيها أنّ التعامل مع العُملاء هو لأمرٌ لا ينتهي عند نقطة مُحددة، بل هو عمليّة مُستمرّة وخبرة مُكتسبة، ومعرفة تراكميّة تزداد مع الوقت، وعندما أقوم بمقارنة بداياتي مع وضعي الحاليّ، أجد أنّي بالفعل قد تطوّرت كثيرًا، ولكنّي إلى الآن لم أصل إلى مرحلة الكمال والمثاليّة. ليس من المُفترض أن يكون العمل كمُستقلّ بذلك الصعوبة، ولكن بنفس الوقت هو ليس سهلًا ويتطلّب العمل بهذا النظام جهدًا لا يُستهان به، وهذا الجهد سيحصد ثماره لا محاله، فعندما يَستثمر المُستقلّ الوقت في التواصل مع العُملاء، فهو يُقوّي من العلاقة بينه وبين العميل ويزيد من الثقة بينهما، هذا التواصل المُثمر هو الّذي سيصب في النتائج النهائيّة للمشروع. ترجمة وبتصرّف للمقال Good Designers, Good Clients لصاحبته Laura Kalbag1 نقطة
-
هل تريد الوصول إلى شبكة الإنترنت بشكلٍ آمن ومحمي من هاتفك الذكي أو حاسوبك المحمول عندما تقوم بالاتصال بشبكاتٍ غير موثوقة مثل الشبكات اللاسلكية العمومية لفندق أو مقهى؟ تسمح لك الشبكة الافتراضية الخاصّة (Virtual Private Network - VPN) بتصفّح الإنترنت بالشبكات غير الموثوقة بشكلٍ آمن ومجهول كما لو كنتَ على شبكةٍ آمنة وموثوقة. يتم تمرير تدفّق البيانات (traffic) من خادومك إلى وجهته ومن ثمّ يعود إليك حاملًا البيانات. يسمح لك هذا الإعداد عندما يتم ربطه مع [اتصال HTTPS] (https://en.wikipedia.org/wiki/HTTP_Secure) أن تقوم بتأمين عمليات تسجيل الدخول وعمليات الدفع وغيرها من العمليات التي تقوم بها عند تصفّحك للإنترنت. يمكنك تخطّي الحجب الذي يفرضه مزوّد الخدمة على مواقع معيّنة مثلًا وتخطّي تقييدات المنطقة الجغرافية لمواقع معيّنة، كما يمكنك تحصين موقعك وتدفّق HTTP الصادر من جهازك من الشبكات غير المحمية. OpenVPN هو عبارة عن شبكة افتراضية خاصّة لطبقة حِزَم البيانات الآمنة مفتوحة المصدر (SSL) ويقبل تشكيلةً واسعة من الإعدادات والتخصيصات. في هذا الدرس، سنقوم بإعداد خادوم OpenVPN على خادومنا ومن ثمّ نقوم بإعداده ليقبل الوصول إليه من نظام ويندوز، Mac OS X، iOS و Android. سنبقي خطوات التثبيت والإعداد أسهل ما يمكن في هذا الدرس. المتطلّباتالمتطلّبات الوحيدة اللازمة هي امتلاك خادوم يعمل بتوزيعة Ubuntu 14.04. يجب أن تمتلك الوصول إلى حساب المستخدم الجذر لإكمال هذا الدرس. إضافي: بعد إكمال هذا الدرس، سيكون من الجيد إنشاء حساب مستخدمٍ عادي بصلاحيات sudo لعمل صيانةٍ عامّة على خادومك في حال احتجتها مستقبلًا.الخطوة الأولى: تثبيت وإعداد بيئة خادوم OpenVPNإعداد OpenVPNقبل أن نقوم بتثبيت أيّ حزم، يجب أن نقوم بتحديث قوائم مستودعات توزيعة أوبونتو أولًا: apt-get updateومن ثمّ، يمكننا تثبيت OpenVPN و Easy-RSA: apt-get install openvpn easy-rsaيجب أن يتم استخراج ملفّ عيّنة إعدادات OpenVPN إلى المسار /etc/openvpn لكي نتمكّن من إضافته إلى عملية التثبيت الخاصّة بنا. يمكن القيام بهذا عبر الأمر: gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.confبمجرّد أن يتم استخراجه، افتح ملفّ server.conf في محرر نصوص. في هذا الدرس سنستخدم Vim ولكن يمكنك استخدم أيّ محرر نصوص تريده: vim /etc/openvpn/server.confهناك العديد من التغييرات التي يجب علينا تطبيقها على هذا الملفّ. سترى قسمًا يبدو هكذا: # Diffie hellman parameters. # Generate your own with: # openssl dhparam -out dh1024.pem 1024 # Substitute 2048 for 1024 if you are using # 2048 bit keys. dh dh1024.pemقم بتغيير dh1024.pem إلى: dh2048.pemسيقوم هذا بمضاعفة طول مفتاح RSA المُستخدم عندما يتم إنشاء مفاتيح الخادوم والعميل. ما نزال في ملفّ server.conf، والآن ابحث عن هذا القسم: # If enabled, this directive will configure # all clients to redirect their default # network gateway through the VPN, causing # all IP traffic such as web browsing and # and DNS lookups to go through the VPN # (The OpenVPN server machine may need to NAT # or bridge the TUN/TAP interface to the internet # in order for this to work properly). ;push "redirect-gateway def1 bypass-dhcp"قم بإلغاء تعليق السطر التالي (أزل إشارة # أو ; من قبله): ;push "redirect-gateway def1 bypass-dhcp" لكي يتمكّن خادوم OpenVPN الخاصّ بنا من تمرير التدفّق المطلوب من المستخدمين والعملاء إلى وجهته المطلوبة. يجب أن يبدو السطر هكذا بعد التعديل: push "redirect-gateway def1 bypass-dhcp"الآن ابحث عن هذا القسم: # Certain Windows-specific network settings # can be pushed to clients, such as DNS # or WINS server addresses. CAVEAT: # http://openvpn.net/faq.html#dhcpcaveats # The addresses below refer to the public # DNS servers provided by opendns.com. ;push "dhcp-option DNS 208.67.222.222" ;push "dhcp-option DNS 208.67.220.220"قم بإلغاء تعليق السطرين push "dhcp-option DNS 208.67.222.222" و push "dhcp-option DNS 208.67.220.220" . يجب أن يبدوا على هذا الشكل: push "dhcp-option DNS 208.67.222.222" push "dhcp-option DNS 208.67.220.220"يقوم هذا بإخبار الخادوم بأن يقوم بدفع OpenVPN ليتصل بالعملاء (clients) ليعطيهم عنوان الـDNS المخصص. يقوم هذا الأمر بحماية المستخدم من خروج طلبات DNS خارج اتصال الـVPN. وعلى كلّ حال، من المهم أن تقوم بتحديد عناوين الـDNS المطلوبة التي تريد استخدامها على كلّ جهازٍ من أجهزة العملاء. رغم أنّ OpenVPN يستخدم خدمة OpenDNS افتراضيًا إلّا أنّه يمكنك استخدامه أيّ خدمات DNS تريدها. القسم الأخير الذي يجب تغييره في ملفّ server.conf هو: # You can uncomment this out on # non-Windows systems. ;user nobody ;group nogroupقم بإلغاء تعليق كلٍّ من السطرين user nobody و group nobody. يجب أن يبدوا هكذا: user nobody group nogroupافتراضيًا، يقوم OpenVPN بالعمل باستخدام المستخدم الجذر (root) حيث يمتلك هذا الأخير وصولًا كاملًا إلى النظام. سنقوم عوضًا عن هذا بجعل OpenVPN يستخدم المستخدم nobody والمجموعة nobody. هذا المستخدم لا يمتلك صلاحيات ولا أيّ إمكانياتٍ للولوج، ويتم استخدامه عادةً لتشغيل التطبيقات غير الموثوقة مثل خواديم واجهات الويب. يمكنك الآن حفظ تغييراتك والخروج من VIM. توجيه حِزَم البياناتهذا الأمر هو عبارة عن خاصية sysctl تقوم بإخبار نواة الخادوم أن تقوم بتوجيه التدفّق من أجهزة العملاء إلى الإنترنت. إن لم يتم هذا الأمر، فإنّ التدفّق سيتوقف في الخادوم. يمكنك تفعيل توجيه حِزَم البيانات أثناء وقت التشغيل باستخدام الأمر التالي: echo 1 > /proc/sys/net/ipv4/ip_forwardنحتاج أن نقوم بجعل هذا الأمر دائمًا بحيث يبقى الخادوم يقوم بتوجيه التدفّق حتى بعد إعادة تشغيل الخادوم: vim /etc/sysctl.confفي بداية ملفّ sysctl ستجد: # Uncomment the next line to enable packet forwarding for IPv4 #net.ipv4.ip_forward=1قم بإلغاء تعليق net.ipv4.ip_forward. يجب أن يبدو ذاك السطر هكذا: # Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1احفظ تغييراتك واخرج. الجدار الناري غير المعقّد (ufw)ufw هو واجهة أمامية (front-end) لـiptables. إعداد ufw ليس صعبًا. وهو مُضمّن بشكلٍ افتراضي في Ubuntu 14.04، لذا يجب علين فقط إنشاء بضع قواعد (rules) وتعديل بعض الإعدادات ليعمل، ومن ثمّ سنقوم بتشغيله. للمزيد من المعلومات حول ufw يمكنك مراجعة كيفية إعداد جدارٍ ناري باستخدام UFW على خادوم أوبونتو ودبيان السحابي. أولًا قم بضبط ufw بحيث يسمح باتصال SSH. أدخل اﻷمر التالي: ufw allow sshسنستخدم OpenVPN عبر UDP في هذا الدرس، لذا يجب أن يسمح UFW بتدفّق UDP عبر المنفذ 1194: ufw allow 1194/udpيجب ضبط سياسة التوجيه في UFW كذلك. يمكنك القيام هذا عبر تعديل ملفّ إعدادات UFW الرئيسي: vim /etc/default/ufwابحث عن DEFAULTFORWARDPOLICY="DROP". يجب تغيير محتوى هذا السطر من DROP إلى ACCEPT. يجب أن يبدو السطر هكذا عندما تنتهي منه: DEFAULT_FORWARD_POLICY="ACCEPT"ثمّ سنقوم بإضافة بعض قواعد UFW الإضافية لترجمة عناوين الشبكة وتذكّر عناوين الـIP الخاصّة بالعملاء الحاليين: vim /etc/ufw/before.rulesقم بجعل بداية ملفّ before.rules الخاصّ بك تبدو هكذا كما بالمثال أدناه. القسم بعد START OPENVPN RULES وقبل END OPENVPN RULES هو ما يجب إضافته: # # rules.before # # Rules that should be run before the ufw command line added rules. Custom # rules should be added to one of these chains: # ufw-before-input # ufw-before-output # ufw-before-forward # # START OPENVPN RULES # NAT table rules *nat :POSTROUTING ACCEPT [0:0] # Allow traffic from OpenVPN client to eth0 -A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE COMMIT # END OPENVPN RULES # Don't delete these required lines, otherwise there will be errors *filterبعدما تمّ تطبيق هذه التغييرات على UFW، يمكننا الآن تفعيله. أدخل اﻷمر التالي في سطر الأوامر: ufw enableوسيتم طباعة الخرج التالي: Command may disrupt existing ssh connections. Proceed with operation (y|n)?للتحقق من حالة قواعد الجدار الناري طبّق: ufw statusوالخرج يجب أن يكون: Status: active To Action From -- ------ ---- 22 ALLOW Anywhere 1194/udp ALLOW Anywhere 22 (v6) ALLOW Anywhere (v6) 1194/udp (v6) ALLOW Anywhere (v6)الخطوة الثانية: إنشاء استيثاق الشهادات و شهادة موقّعة من الخادوم ومفتاح لهايستخدم OpenVPN الشهادات ليقوم بتشفير التدفّق. إعداد وبناء استيثاق الشهاداتالآن هو وقت إعداد استيثاق الشهادات الخاصّ بنا (Certificate Authority - CA) وإنشاء شهادة ومفتاح لخادوم الـOpenVPN. يدعم OpenVPN الاستيثاق ثنائي الاتجاه (bidirectional authentication) باستخدام الشهادات، مما يعني أنّه يجب على العميل أن يقوم باستيثاق شهادة الخادوم ويجب على الخادوم أن يقوم باستيثاق شهادة العميل قبل أن يتم إنشاء الاتصال المشترك بينهما. سنستخدم سكربتات Easy RSA التي نسخناها مبكّرًا للقيام بهذا. أولًا، انسخ سكربتات Easy RSA إلى مكانها: cp -r /usr/share/easy-rsa/ /etc/openvpnثمّ قم بعمل مجلّد لتخزين المفاتيح: mkdir /etc/openvpn/easy-rsa/keysتمتلك Easy-RSA ملفّ متغيّرات يمكننا تعديله لإنشاء شهاداتٍ خاصّة بشخصنا، شركاتنا أو أيّ شيءٍ آخر نريده. يتم نسخ هذه المعلومات إلى الشهادات والمفاتيح، وستساعد هذه المعلومات على التعرّف على المفاتيح لاحقًا: vim /etc/openvpn/easy-rsa/varsالمتغيّرات بين علامتيّ تنصيص (") يجب أن تقوم بتغييرها بناءً على إعداداتك: export KEY_COUNTRY="US" export KEY_PROVINCE="TX" export KEY_CITY="Dallas" export KEY_ORG="My Company Name" export KEY_EMAIL="sammy@example.com" export KEY_OU="MYOrganizationalUnit"في نفس الملفّ vars، قم أيضًا بتعديل السطر الظاهر أدناه، سنستخدم server كاسم المفتاح. إذا أردت استخدام اسمٍ آخر، فسيجب عليك أيضًا تحديث ملفّات إعدادات OpenVPN التي تقوم بالإشارة إلى server.key و server.crt: export KEY_NAME="server"نحتاج إلى إنشاء مُعامِلات Diffie-Hellman; قد يأخذ هذا الأمر بضع دقائق: openssl dhparam -out /etc/openvpn/dh2048.pem 2048الآن فلنغيّر المسارات بحيث نعمل في المسارات الذي نقلنا سكربتات Easy-RSA إليه من قبل في الخطوة الثانية: cd /etc/openvpn/easy-rsaقم بتحليل ملفّ (PKI (Public Key Infrastructure. انتبه إلى النقطة (.) و المسافة (space) قبل الأمر vars/. حيثُ أنّ هذا الأمر قد يغيّر المسار الحالي كلّيًا (المصدر): . ./varsخرج الأمر أعلاه ظاهرٌ أدناه. بما أننا لم نقم بإنشاء أيّ شيء في مجلّد keys بعد، فإنّ رسالة التحذير ليست شيئًا لنقلق حوله الآن: NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/keysالآن سنقوم بتنظيف مسار عملنا الحالي من أيّ ملفّات عيّنة مفاتيح أو مفاتيح قديمة لكي تبقى المفاتيح الجديدة بالمسار فقط: ./clean-allسيبني هذا الأمر الأخير استيثاق الشهادات (CA) عبر استدعاء طرفيّة OpenSSL التفاعلية. سيسألك الخرج أن تقوم بتأكيد اسم المتغيرات الفريدة التي أدخلناها من قبل في ملفّ متغيّرات Easy-RSA (اسم البلد، المنظمة.. إلخ): ./build-caاضغط مفتاح Enter ببساطة لتتنقل عبر كلّ طرفية. إذا كان هناك شيءٌ يجب تغييره، فيمكنك القيام بذلك عبر سطر الأوامر. إنشاء شهادة ومفتاح للخادومما زلنا نعمل من المسار /etc/openvpn/easy-rsa ، الآن قم بإدخال الأمر التالي لبناء مفتاح الخادوم. حيث سترى أنّ server هو قيمة المتغيّر export KEY_NAME الذي أعددناه في ملفّ vars الخاصّ بـEasy-RSA من قبل بالخطوة الثانية: ./build-key-server serverيظهر خرج مشابه كالذي يظهر عندما طبّقنا ./build-ca ، ويمكنك مجددًا الضغط على مفتاح Enter لتأكيد كلّ سطرٍ من الأسماء الفريدة. على كلّ حال، هذه المرّة هناك إدخالان إضافيان: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:يجب أن يتم تركهما كلاهما فارغين، لذا فقط اضغط Enter للمتابعة. هناك سؤالان إضافيان في النهاية، وهما يتطلبان إجابة ( y ) إيجابية فقط: Sign the certificate? [y/n] 1 out of 1 certificate requests certified, commit? [y/n]يجب أن يكتمل السؤال الأخير أعلاه بالتالي: Write out database with 1 new entries Data Base Updatedنقل شهادات الخادوم والمفاتيحيتوقّع OpenVPN أن يرى استيثاق شهادات الخادوم، الشهادات والمفاتيح في /etc/openvpn . فلننسخها إلى الموقع الصحيح: cp /etc/openvpn/easy-rsa/keys/{server.crt,server.key,ca.crt} /etc/openvpnيمكنك التحقق من نجاح العملية عبر: ls /etc/openvpnيجب أن ترى ملفّات الشهادات والمفاتيح الخاصّة بالخادوم. في هذه النقطة، أصبح خادوم OpenVPN جاهزًا للانطلاق. شغّله وتحقق من حالته باستخدام: service openvpn start service openvpn statusيجب أن يقوم أمر التحقق من الحالة بإرجاع الخرج التالي لك: VPN 'server' is runningتهانينا! أصبح خادوم OpenVPN الخاصّ بك عاملًا! إذا كانت رسالة الحالة تقول لك أنّ خادوم الـOpenVPN لا يعمل، فتحقق من ملفّ /var/log/syslog وابحث عن أخطاء مثل: Options error: --key fails with 'server.key': No such file or directoryوالتي تقوم أنّ ملفّ server.key لم يتم نسخه إلى المسار etc/openvpn/ بشكلٍ صحيح، مما يتوجب عليك إعادة نسخه مجددًا. الخطوة الثالثة: إنشاء الشهادات والمفاتيح الخاصّة بالعملاءلقد قمنا بتثبيت وإعداد خادوم OpenVPN، أنشأنا استيثاق الشهادات وشهادة ومفتاح الخادوم. في هذه الخطوة الآن، سنستخدم استيثاق شهادات الخادوم لنقوم بإنشاء الشهادات والمفاتيح الخاصّة بجهاز كلّ عميلٍ سيتصل بالخادوم على حدى. سيتم تثبيت هذه الملفّات لاحقًا إلى أجهزة العملاء كالهواتف الذكية والأجهزة المحمولة. بناء المفاتيح والشهاداتمن المناسب أن يمتلك كلّ عميلٍ يتصل بالخادوم شهادته ومفتاحه الخاصّين به. هذا الأمر مفضّل على إنشاء شهادة واحدة ومفتاح واحد وتوزيعهما على جميع أجهزة العملاء لأغراض تتعلق بالأمان. ملاحظة: افتراضيًا، لا يسمح OpenVPN بالاتصالات المتزامنة بنفس الوقت إلى الخادوم من أكثر من جهازٍ عميل باستخدام نفس الشهادة والمفتاح (انظر duplicate-cn في etc/openvpn/server.conf/). لإنشاء شهادات استيثاق منفصلة لكلّ جهاز عميل تنوي السماح له بالاتصال بخادوم الـOpenVPN، يجب عليك إكمال هذه الخطوة لكلّ جهاز، ولكن يمكنك تغيير الاسم client1 أدناه إلى شيءٍ مختلف مثل client2 أو iphone2. بفضل استخدام شهادةٍ منفصلة لكلّ جهاز عميل، يمكن لاحقًا منعهم بشكلٍ منفصل من الوصول إلى الخادوم إذا ما تمّ الاحتياج لذلك. سنستخدم client1 في الأمثلة الموجودة في هذا الدرس كاسم الجهاز العميل. كما فعلنا مع مفتاح الخادوم، فسنقوم الآن ببناء واحد لمثال client1. يجب أن تكون ما زلت تعمل في /etc/openvpn/easy-rsa: ./build-key client1مجددًا، سيتم سؤالك عمّا إذا كنتَ تريد تغيير أو تأكيد متغيّرات الأسماء الفريدة والحقول الإضافية التي تركناها فارغة من قبل. اضغط Enter لاختيار الخيارات الافتراضية: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:وكما في السابق، سيسألك مرتين عن تأكيد بعض الخيارات، اكتب y: Sign the certificate? [y/n] 1 out of 1 certificate requests certified, commit? [y/n]إذا نجحت عملية بناء المفتاح فسيظهر التالي: Write out database with 1 new entries Data Base Updatedيجب على ملفّ عيّنة إعدادات العميل أن يتم نسخه إلى مجلّد Easy-RSA أيضًا. سنستخدمه كقالب يمكن تحميله إلى أجهزة العملاء ليتم تعديله. أثناء عملية النسخ، سنقوم بتغيير اسم ملفّ العيّنة من client.conf إلى client.ovpn لأنّ امتداد ملفّ .ovpn هو ما تتوقع أجهزة العملاء أن تراه باسم الملفّ وليس .conf: cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/easy-rsa/keys/client.ovpnيمكنك تكرار هذا القسم مجددًا لكلّ عميلٍ تريد السماح له بالوصول، ولا تنسى استبدال client1 باسم الجهاز الجديد. نقل الشهادات والمفاتيح إلى أجهزة العميلخلاصة ما طبقّناه أعلاه هو أننا أنشأنا شهادات ومفاتيح العملاء، وأننا قمنا بتخزينها على خادوم OpenVPN في المسار /etc/openvpn/easy-rsa/keys . نحتاج القيام بنقل شهادة العميل، المفتاح وقالب الملفّ الشخصي لكلّ عميلٍ نريد السماح له بالوصول إلى مجلّد محلّي على جهاز الحاسوب الخاصّ بنا أو أيّ جهاز عميل آخر. في هذا المثال، يتطلّب جهاز العميل client1 الشهادات والمفاتيح الخاصّة به والموجودة في المسارات: * /etc/openvpn/easy-rsa/keys/client1.crt * /etc/openvpn/easy-rsa/keys/client1.keyتكون ملفّات ca.crt و client.ovpn هي نفسها لجميع أجهزة العملاء مهما اختلفت. قمّ بتحميل هذه الملفّات أيضًا; لاحظ أنّ ملف ca.crt هو في مسارٍ مختلف عن الملفّات الأخرى: etc/openvpn/easy-rsa/keys/client.ovpn/etc/openvpn/ca.crt/بينما ستعتمد التطبيقات المُستخدمة لإكمال هذه المهمّة على اختيارك ونوع نظام التشغيل الخاصّ بك، فإنّك بحاجة إلى تطبيق يستخدم STFP (بروتوكول نقل الملفّات عبر SSH) أو SCP (النسخ الآمن) للوصول إلى الملفّات الموجودة في خلفية الخادوم. سينقل هذا ملفّات الاستيثاق الخاصّة بخادومك عبر اتصالٍ مشفّر. إليك مثالًا لأمر SCP باستخدام مثالنا من client1. إنّه يقوم بوضع الملفّ client1.key إلى المجلّد Downloads الموجود على حاسوبنا المحلّي: scp root@your-server-ip:/etc/openvpn/easy-rsa/keys/client1.key Downloads/إليك بضع أدوات ودروس يمكنك استخدامها لنقل الملفّات بشكلٍ آمن من الخادوم إلى الحاسوب المحلّي: WinSCPكيفية استخدام SFTP لنقل الملفّات بشكلٍ آمن من خادومٍ بعيد.كيفية استخدام FileZilla لنقل وإدارة الملفّات بشكلٍ آمن.في نهاية هذا القسم، تأكّد أنّ هذه الملفّات الأربعة أصبحت موجودة على جهاز المحلّي مهما كان نوعه: client1.crtclient1.keyclient.ovpnca.crtالخطوة الرابعة: إنشاء ملفٍّ شخصيٍ موحّد لجهاز العميلهناك العديد من الطرق المُستخدمة لإدارة ملفّات العملاء ولكن أسهلها يستخدم ملفّا شخصيًا موحّدًا. يتم إنشاء هذا الملفّ عبر تعديل قالب ملفّ client.ovpn ليتضمّن استيثاق شهادات الخادوم، شهادة العميل والمفتاح الخاصّ به. بمجرّد إضافته، فسيجب حينها فقط استيراد ملفّ client.ovpn إلى تطبيقات OpenVPN على أجهزة العميل. سنقوم بإنشاء ملفٍّ شخصيٍ وحيد لجهازنا المدعو client1 على الحاسوب المحلّي الذي قمنا بتحميل جميع الملفّات إليه. يمكن لهذا الحاسوب المحلّي بنفسه أن يكون جهازًا عميلًا أو مجرّد منطقة عمل مؤقّتة لدمج ملفّات الاستيثاق. يجب أن يتم نسخ قالب ملفّ client.ovpn وإعادة تسميته. كيفية قيامك بهذا الأمر ستعتمد على نظام التشغيل الخاصّة بجهازك المحلّي. ملاحظة: لا يجب على اسم ملفّ client.ovpn الخاصّ بك المنسوخ الجديد أن يكون مطابقًا لاسم جهاز العميل. تطبيق العميل لـOpenVPN سيستخدم اسم الملفّ كمُعرّف لاتصال VPN نفسه، حيث يجب عليك نسخ ملفّ client.ovpn إلى أيّ اسمٍ جديد تريد استخدامه على نظام التشغيل الخاصّ بك. كمثال: work.ovpn سيتم التعرّف عليه كـwork و school.ovpn سيتم التعرّف عليه كـschool. في هذا الدرس، سنقوم بتسمية اتصال VPN كـ HsoubAcademy ولذلك فسيكون اسم الملفّ هو HsoubAcademy.ovpn من الآن فصاعدًا. بمجرّد تسمية الملفّ الجديد المنسوخ، يجب علينا حينها فتح ملفّ HsoubAcademy.ovpn في محرر نصوص. أول نقطة سيقع عليها انتباهك هي مكان عنوان الـIP الخاصّ بخادومك. بالقرب من بداية الملفّ، قم بتغيير my-server-1 إلى عنوان IP الخاصّ بخادومك: # The hostname/IP and port of the server. # You can have multiple remote entries # to load balance between the servers. remote my-server-1 1194الآن، ابحث عن القسم الظاهر أدناه وقم بإلغاء تعليق user nobody و group nobody، كما فعلنا مع ملفّ server.conf في الخطوة الأولى. ملاحظة: هذا لا ينطبق على ويندوز لذا يمكنك تجاوزه. يجب أن يبدو هكذا عندما تنتهي: # Downgrade privileges after initialization (non-Windows only) user nobody group nogroupالقسم الظاهر أدناه يحتاج أن نقوم فيه بتعليق السطور الثلاثة الأخيرة لكي نتمكّن من تضمين الشهادة والمفتاح مباشرةً في ملفّ HsoubAcademy.ovpn، يجب أن يبدو هكذا: # SSL/TLS parms. # . . . #ca ca.crt #cert client.crt #key client.keyلدمج الملفّات المنفصلة إلى ملفٍّ شخصي واحدٍ موحّد، يجب أن يتم وضع محتويات الملفّات ca.crt, client1.crt, و client1.key مباشرةً في ملفّ .ovpn باستخدام هيكلة XML. يجب بالنهاية أن يبدو شكلها كالتالي: <ca> (أدخل محتويات ca.crt هنا) </ca> <cert> (أدخل محتويات client1.crt هنا) </cert> <key> (أدخل محتويات client1.key هنا) </key>عندما تنتهي، يجب أن تبدو نهاية الملفّ كالتالي: <ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </ca> <cert> Certificate: . . . -----END CERTIFICATE----- . . . -----END CERTIFICATE----- </cert> <key> -----BEGIN PRIVATE KEY----- . . . -----END PRIVATE KEY----- </key>يمتلك ملفّ client1.crt معلوماتٍ إضافية في داخله; لا بأس بتضمين الملفّ بأكمله. احفظ التغييرات واخرج. أصبح لدينا الآن ملفّ واحد موحّد لضبط جهازنا المحلّي client1. الخطوة الخامسة: تثبيت ملفّ العميلالآن سنناقش تثبيت ملفّ VPN على Windows, OS X, iOS, و Android. لا يعتمد أيٌّ من إرشادات هذه الأنظمة على الآخر لذا يمكنك التجاوز إلى الذي يطابق نظامك التشغيلي الحالي فقط. تذكّر أنّ الاتصال سيتم تسميته بحساب اسم ملفّ .ovpn الذي استخدمته. في مثالنا قمنا بتسمية الملفّ كـ HsoubAcademy.ovpn ولذلك سيكون اسم الاتصال هو HsoubAcademy. Windowsالتثبيتيمكن العثور على تطبيق عميل OpenVPN لنظام ويندوز من صفحة التحميلات الخاصّة بـOpenVPN. اختر المُثبّت المناسب لإصدارك من ويندوز. ملاحظة: يتطلّب OpenVPN الصلاحيات الإدارية ليتم تثبيته. بعد تثبيته، انسخ الملفّ HsoubAcademy.ovpn إلى: C:\Program Files\OpenVPN\configعندما تقوم بتشغيل OpenVPN، فسيقوم تلقائيًا برؤية الملفّ وجعله متاحًا. يجب أن يتم تشغيل OpenVPN بصلاحيات المسؤول في كلّ مرّة يتم تشغيله، حتى ولو كان يتم تشغيله على حسابات المستخدمين. لفعل هذا دون الحاجة إلى الضغط على زرّ الفأرة الأيمن واختيار التشغيل كمسؤول في كلّ مرّة تقوم باستعمال الـVPN، يمكنك ضبط هذا الأمر ولكنك ستحتاج إلى القيام به من حساب بصلاحياتٍ إدارية. هذا يعني أيضًا أنّه سيجب على المستخدمين إدخال كلمة مرور المستخدم المدير لاستخدام OpenVPN. على الناحية الأخرى، لن يتمكن المستخدمون من استخدام OpenVPN بشكلٍ صحيح دون أن يتم تشغيله على جهاز العميل بصلاحياتٍ إدارية، لذا فإنّ الصلاحيات الإدارة مطلوبة. لجعل تطبيق OpenVPN يتم تشغيله دومًا كمدير، اضغط بزرّ الفأرة الأيمن على أيقونة الاختصار الخاصّة به واذهب إلى خصائص. في نهاية لسان التوافقية، اضغط على الزرّ لتغيير الإعدادات لجميع المستخدمين. في النافذة الجديدة، اختر تشغيل هذا البرنامج كمدير. الاتصالفي كلّ مرّة تقوم بتشغيل واجهة OpenVPN الرسومية، سيسألك ويندوز عمّا إذا كنتَ تريد السماح للبرنامج بتطبيق التغييرات على نظامك. اضغط على موافق. تشغيل تطبيق عميل OpenVPN سيقوم فقط بوضع الأيقونة في تنبيهات النظام ويمكن الاتصال أو قطع الاتصال بخادوم OpenVPN عند الحاجة; وهو لا يقوم حقيقةً بالاتصال بـ VPN. بمجرّد بدء OpenVPN، قم بإنشاء اتصال عبر الذهاب إلى تنبيهات النظام والضغط بزرّ الفأرة الأيمن على أيقونة OpenVPN. هذا سيفتح لك قائمةً فرعية. اختر HsoubAcademy من أعلى القائمة (والذي هو عبارة عن HsoubAcademy.ovpn) واختر اتصال. سيتم فتح نافذة حالة جديدة تعرض لك الخرج أثناء تأسيس الاتصال، وسيتم طباعة رسالة بمجرّد اكتمال العملية. يمكنك إلغاء الاتصال من VPN بنفس الطريقة: اذهب إلى تنبيهات النظام وانقر بزرّ الفأرة الأيمن على أيقونة OpenVPN واختر قطع الاتصال. OS XالتثبيتTunnelblick هو تطبيق عميل OpenVPN مجاني ومفتوح المصدر لنظام MAC OS X. يمكنك تحميله من صفحة تحميلات Tunnelblick. انقر نقرتين على ملفّ .dmg المحمّل وتابع إرشادات التثبيت. إلى نهاية عملية التثبيت، سيسألك Tunnelblick عمّا إذا كنتَ تمتلك أيّ ملفّات إعدادات. الأسهل حاليًا هو اختيار لا وترك Tunnelblick ليتابع التثبيت. ابحث نافذة Finder وانقر على ملفّ HsoubAcademy.ovpn وسيتم تشغيل Tunnelblick باستخدام ذلك الملفّ، الصلاحيات الإدارية مطلوبة. الاتصالشغّل Tunnelblick عبر النقر مرّتين على مجلّد أيقونة التطبيقات. بمجرّد تشغيل Tunnelblick، سيكون هناك أيقونة Tunnelblick على شريط القائمة في أعلى يمين الشاشة للتحكّم بالاتصالات. اضغط على الأيقونة ومن ثمّ عنصر قائمة اتصال لبدء اتصالٍ جديد واختر HsoubAcademy. iOSالتثبيتمن متجر تطبيقات iTunes، ابحث وثبّت تطبيق OpenVPN Connect، والذي هو تطبيق عميل OpenVPN الرسمي لـiOS. لنقل ملفّ .ovpn الخاصّ بك إلى هاتفك، قم بتوصيله مباشرةً إلى حاسوب. إكمال النقل مع iTunes سيكون خارجيًا هنا. افتح iTunes من على الحاسوب واضغط على iPhone > apps. انتقل إلى الأسفل إلى قسم مشاركة الملفّات وانقر على تطبيق OpenVPN. النافذة الموجودة على اليمين، OpenVPN Documents هي لمشاركة الملفّات. اسحب ملفّ .ovpn وأفلته إلى النافذة أدناه. الآن شغّل تطبيق OpenVPN على iPhone. سيكون هناك إشعار أنّه هناك ملفّ جديد ليتم استيراده. انقر على إشارة الزائد الخضراء ليتمّ ذلك: الاتصالأصبح OpenVPN الآن جاهزًا للاستخدام مع الملفّ الجديد. ابدأ الاتصال عبر النقر على زرّ Connect وجعله On. ويمكنك قطع الاتصال عبر جعله Off. ملاحظة: لا يمكن استخدام محوّل VPN الموجود تحت الإعدادات للاتصال بـVPN. إذا حاولت، فسيصلك إشعار باستخدام تطبيق OpenVPN فقط. Androidالتثبيتافتح متجر Google Play. ابحث عن تطبيق Android OpenVPN Connect وثبّته، إنّه تطبيق OpenVPN الرسمي لأندرويد. يمكن نقل ملفّ .ovpn إلى الهاتف عبر إيصال جهاز الأندرويد الخاصّ بك إلى الحاسوب عبر USB ونقل الملفّ. بشكلٍ مغاير، إذا كنتَ تمتلك قارئ بطاقات SD، فيمكنك إزالة بطاقة SD من الجهاز، نسخ الملفّ إليها ومن ثمّ إدراجها مجددًا إلى جهاز الأندرويد. ابدأ تطبيق OpenVPN وانقر على القائمة للاستيراد: ثمّ تنقّل إلى موقع الملفّ المحفوظ (لقطة الشاشة تستخدم /sdcard/Download/) واختر الملفّ. وسيقوم التطبيق بإخبارك أنّ الملفّ تمّ استيراده: الاتصالللاتصال، قم ببساطة بالضغط على زرّ الاتصال الموجود أمامك. سيتم سؤالك عمّا إذا كنتَ تثق بالتطبيق، اضغط على موافق وتابع. لإلغاء الاتصال، انقر على زرّ قطع الاتصال بالتطبيق. الخطوة السادسة: اختبار اتصالك بـ VPNبمجرّد تثبيت كلّ شيء، يمكن لفحصٍ سريع أن يؤكّد لك أنّك تستعمل VPN الآن أو لا. أولًا قبل استخدام أيّ VPN، اذهب إلى موقع DNSLeakTest. سيعطيك الموقع عنوان الـIP الخاصّ بك عبر مزوّد الإنترنت لديك وستظهر أنت كما تبدو لبقية العالم. للتحقق من إعدادات DNS الخاصّ بك عبر نفس الموقع، انقر على Extended Test وسيخبرك عن خواديم DNS التي تستعملها. الآن، قم بالاتصال بخادوم OpenVPN الموجود على خادومك وحدّث الصفحة. وشاهد عنوان الـIP الجديد المختلف كليًا عن السابق. هذا سيكون هو عنوان الـIP الظاهر للعالم عند تصفّحك لمواقعهم. مجددًا، انقر على Extended Test وسيخبرك عن خواديم DNS التي تستعملها وسيؤكد لك أنّك تستخدم الآن خواديم الـDNS المُعدّة من قبل OpenVPN. تهانينا! يمكنك الآن التنقّل بأمان في الإنترنت وحماية خصوصيتك، موقعك وبياناتك من المراقبة والمُخترقين. ترجمة -وبتصرّف- للمقال: How To Set Up an OpenVPN Server on Ubuntu 14.04.1 نقطة
-
الانتشار الكبير في استخدام لغات تصميم الويب أدى إلى ظهور بعض إطارات العمل (Frameworks) الجاهزة التي تسهل وتسرع عملية تطوير الواجهات كذلك فإن الانتشار الواسع لووردبريس والطلب الكبير لقوالبه دفع بعض المطورين والشركات إلى تطوير العديد من الأدوات الجاهزة التي تسهل بصورة أو أخرى عملية تطوير قوالب ووردبريس، إحدى هذه الأدوات الجيدة جدا هي قالب Underscores _s والذي يتمتع بمجموعة من المميزات (كما سنرى) تجعله من أفضل الأدوات المعينة لمطور قوالب ووردبريس. وفي هذا المقال سنتعرف على بعض مميزات قالب _s وطريقة استخدامه والاستفادة منه، لكن دعنا في الأول نلقي نظرة سريعة على بعض الأدوات المتاحة لمطوري قوالب ووردبريس (من قبل شركات أو مطورين أخرين) وأنواعها وفي أي الأنواع يمكن تصنيف قالب Underscores _s. أطر عمل قوالب ووردبريس WordPress Theme Frameworksأتذكر أنه عند بداية دراستي لتطوير قوالب ووردبريس شكل هذا المصطلح بعض الصعوبة بالنسبة لي ليس لأنه يحتوي على كلمة إطار عمل (Framework) فقط بل لأنه في مجتمع ووردبريس يطلق هذا المصطلح على عدة معاني حيث يختلف المعنى الذي يستخدم لأجله من بعض المطورين، لذا دعنا نحاول تجنيبك الوقوع في مثل هذا الإشكال بتعريف مبسط لكلمة إطار عمل وكذلك الإشارة الى بعض المعاني التي تستخدم لها هذه الكلمة في مجتمع ووردبريس. التعريف المبسط لإطار العمل Framework في مجال تطوير الويبهو عبارة عن مجموعة من الأدوات الجاهزة (عادة شيفرات برمجية) تؤدي بعض المهام الشائعة وتغنيك هذه الإطارات من كتابة هذه الشيفرات بنفسك. بالطبع فإن التعريف يمكن أن يكون أعم من ذلك لكن هذا ما نحتاجه في هذا المقال. إطارات عمل قوالب ووردبريس Themes Frameworkيطلق هذا المصطلح عادة على أربعة معاني في مجتمع ووردبريس (موقع ووردبريس الرسمي ذكر ثلاثة ودعني أضيف لها رقم 2 في قائمتنا هذه). القالب الأب Parents Themes (البعض يطلق عليها Themes Framework) هذا النوع من الأدوات عبارة عن قالب جاهز يمكنك الاستفادة منه من خلال صنع قالبك كقالب ابن Child Theme له وذلك بالاستفادة من هذه الميزة (القالب الابن) التي يوفرها ووردبريس. عادة عند إطلاق كلمة إطار عمل قوالب Themes Framework فإن المقصود هو هذا النوع من القوالب، ومن أشهر هذه القوالب هو إطار العمل Genesis ومن الإطارات ذات التطوير العربي هنالك إطار العمل ممتاز وليس المقام هنا لسرد القوالب المتوفرة تحت هذا النوع من الأدوات.إطارات عمل لوحة تحكم القوالب Theme Options Framework هذا النوع من الأدوات كما يظهر من أسمه يستخدم لتسهيل مهمة إنشاء لوحة تحكم للقالب فهو يسهل عملية إضافة واسترجاع خيارات القالب وعادة ما يوفر مجموعة من الحقول المتقدمة التي تتطلب برمجتها بدونه بعض الوقت والجهد. البعض أيضا يطلق كلمة إطار عمل (هكذا بإطلاقها) على هذا النوع من الأدوات، ومن أشهر هذه الإطارات إطار عمل Redux.المكتبات المساعدة Dropin Libarary هذه مجموعة من الملفات التي تقوم بتضمينها في قالبك للاستفادة من الدوال والأصناف الموجودة فيها وبالرغم من أن كثير من إطارات عمل لوحة التحكم يمكنها العمل بنفس الطريقة (تضمين ملفاتها في قالبك) لكن هذه المكتبات توفر عادة أكثر من مجرد تسهيل لعمل لوحة التحكم بل تتيح لك المزيد من التسهيلات فيما يتعلق بتطوير القالب ككل. من هذه المكتبات إطار العمل Hyprid.القالب الابتدائي Starter Theme عبارة عن قالب جاهز بنسبة كبيرة تقوم بتطوير قالبك بالتعديل عليه مباشرة لا عن طريق استخدام قالب ابن كما في الرقم 1 من هذه القائمة، ويوفر هذا القالب عادة التركيبة الأساسية لملفات قوالب ووردبريس وبعض الدوال والإعدادات الأساسية للقالب بحيث يجعل المصمم يركز على التصميم (CSS) أو تحويل التصميم الجاهز. قالب Underscores والذي نحن بصدد التعرف عليه يقع تحت هذا النوع من إطارات العمل وتستطيع استخدامه في تطوير قالبك من الخلال التعديل المباشر عليه كما سنرى لاحقا إن شاء الله.مميزات قالب Underscoresهنالك عدة مميزات تجعل من الجيد إختيار هذا القالب كنقطة بداية لتطوير قالبك، ومن هذه المميزات: القالب مفتوح المصدر ومطور من قبل مجموعة من المبرمجين المحترفين وعلى رأس التطوير شركة Automatic الراعية لووردبريس، وهذا يضمن لك توافقية عالية بين ووردبريس وقالب Underscores وكذلك احترافية في تطويره.القالب يعتمد على HTML5 كما أنه موثق بصورة جيدة ويحتوي على نسبة مناسبة من الشيفرات (بالتالي لن تفقد الكثير من الأساسيات الموجودة في أي قالب كما أنك لن تضطر لمسح الكثير من الشيفرات).يحتوي على مثال لتطبيق خاصية الترويسة المخصصة Custom header التي يوفرها ووردبريس.مجموعة مخصصة من وسوم القالب Template Tags (يشير هذا المصطلح الى مجموعة من الدوال تستخدم في جلب بيانات المقال) يمكنك استخدامها لتجنب تكرار الشيفرات البرمجية.مجموعة من تنسيقات CSS الأساسية تحتوي على توضيح لبعض فئات العناصر CSS Classes التي يولدها ووردبريس.يتبع الCoding standard الخاصة بووردبريس وموثق بصورة ممتازة، حيث يمكنك أن تتعلم الكثير منه.مجموعة من الدوال والإعدادات الأساسية في ملف functions.php.يشجع على استخدام مخصص القوالب Theme Customizer وهذا أمر مهم جدا خصوصا وأنه قبل أيام قليلة من نشر هذا المقال أصبح إستخدام مخصص القوالب لإعدادات القالب أمر إجباري لكل القوالب في مستودع القوالب الخاص بووردبريس وهذا قد يشير إلى توجه في مجتمع ووردبريس نحو المخصص ومحاولة جعلها Standerd في مسألة خيارات القالب، ولذلك في نهاية هذه السلسلة سنلقي نظرة إلى طريقة إستخدام مخصص القوالب في خيارات القالب.طريقة استخدام Underscoresفي البداية عليك التوجه لموقع القالب وقم بكتابة اسم القالب الذي تود تطويره في الخانة المخصصة ويمكنك الضغط على Advanced Options لإدخال المزيد من الخيارات مثل اسم المطور ورابط موقعه ووصف القالب، وسيقوم الموقع باستخدام هذه المعلومات ويولد لك القالب والذي سيبدأ التحميل مباشرة فور الضغط على Generate. طبعا ستجد أن المعلومات التي قمت بكتابتها تم استخدامها في الترويسة الأساسية للقالب والموجودة في ملف style.css وكذلك في اسم مجلد القالب. بعد ذلك يمكنك تنصيب القالب على الموقع الذي قمت بإعداده لتطوير القالب ومن الجيد أن تقوم بفتح مجلد القالب على محرر الشيفرات المفضل بالنسبة لك حتى تتابع معنا شرح بعض خصائص القالب. التركيبة الأساسية للقالب (المجلدات والملفات)ستلاحظ أن مجلد القالب يحتوي على الملفات الأساسية لقوالب ووردبريس وهي: Style.css, index.php, functions.php, single.php, page.php, archive.php, search.php, 404.php, header.php, footer.php, sidebar.php, comments.php, rtl.css بالإضافة الى مجموعة من الملفات التي يبدأ اسمها بـ content وهي: Content.php, content-single.php, content-page.php, content-search.php, content-none.phpوهذه الملفات تستخدم لوضع محتويات الحلقة The Loop لبعض ملفات القالب الأساسية التي تحتوي على حلقة لجلب المقالات مثل ملف single.php مثلا، حيث يتم تضمين ملف conent المناسب (مثلا content-single.php) في ملف القالب المناسب (single.php) من خلال إستخدام الدالة get_template_part وتستخدم بهذه الطريقة: <?php get_template_part( 'content', 'single' ); ?> حيث أن المعامل الأول للدالة هو بداية اسم الملف والمعامل الثاني هو نهايته (ما بعد الفاصلة) وهذا يوضح لك سبب تسمية الملفات التي تبدئ بـ content بهذه الطريقة. واستخدام هذه الدالة من الأمور الجيدة في هذا القالب لما تتميز به من مميزات وحتى تحافظ على مقروئية جيدة للشيفرة البرمجية فبدلا من وضع كل الشيفرات في ملف single.php او index.php يتم فصل محتويات الحلقة -والتي عادة ما تحتوي على الكثير من شيفرات html- في ملف لوحدها ثم تضمينها. وأيضا يمكنك استخدامها مع الدالة get_post_format لتضمين الملفات اعتمادا على بنية المقال. من الملفات أيضا في المجلد الرئيسي للقالب ملف README.md وصورة screenshot.png ولا أظن أنهما يحتاجان إلى شرح. مجلد incهذا أو المجلدات التي سنتحدث عنها ، حيث يحتوي على خمسة ملفات الجامع بينها أنه يتم تضمينها داخل ملف function.php كما ترى في نهاية هذا الملف من خلال استخدام الدالة بهذه الطريقة: require get_template_directory() . '/inc/template-tags.php'; حيث أن الدالة ()get_template_directory ترجع مسار القالب الحالي ومن الجيد استخدام دوال ووردبريس التي يوفرها فيما يتعلق بمسارات القالب، وهذه أحد الأشياء التي عليك تعلمها من قالب Underscores بالإضافة الى العديد من الممارسات الجديدة Best Practice الأخرى كما سنرى. تضمين هذه الملفات في ملف functions.php بدلا عن كتابة كل الشيفرات الوظائفية الخاصة بالقالب في ملف functions.php يجعل القالب أكثر تنظيما ويسهل التعديل عليه فيما بعد بالإضافة لفصل الدوال والشيفرات على حسب وظائفها. لاحقا سنتطرق الى تفاصيل بعض هذه الملفات بعد أن نلقي نظرة سريعة على ملف functions.php، لكن الأن دعنا نكمل الاطلاع على تركيبة القالب. مجلد jsواضح من اسمه أنه يحتوي على ملفات الجافا سكربت الخاصة بالقالب، وبه ثلاث ملفات سنطلع على وظائفها فيما الدرس القادم إن شاء الله. وبالطبع يمكنك إضافة ملفات جافا سكربت الخاصة بك في هذا المجلد. لاحظ أنك فيما بعد قد تحتاج لإضافة مجلد لملفات CSS واخر للصور وفي هذه الحالة ربما تفضل وضع هذين المجلدين مع مجلد js في مجلد جديد باسم assest أو static مثلا. بقية المجلداتكما تلاحظ هنالك أيضا مجلد language الذي به ملفات اللغة، وملف layout وهو يحتوي على ملفي CSS يستخدمان لتوفير خيارين في تخطيط الموقع، وملف sass وبه بعض الملفات المفيدة في حالة كنت تستخدم SASS. خاتمة كانت هذه مقدمة سريعة على أنواع الإطارات الخاصة بعمل قوالب ووردبريس، ونظرة على طريقة تحميل قالب _s وتركيبة الملفات والمجلدات فيه، وتبقى لنا الكثير لمعرفته حول قالب _s وإستخدامه في تطوير قوالب ووردبريس وهو ما سنتطرق إليه في المقالات القادمة من هذه السلسلة إن شاء الله.1 نقطة
-
لماذا إعادة تقديم؟ لسوء سُمعة JavaScript حيثُ أنَّها أكثر لُغة برمجة يُساء فهمها في العالم. على الرَّغم من أنَّه في كثيرٍ من الأحيان يتمّ وصفها لُعبة على سبيل السخريّة، إلَّا أنّهُ أسفل بساطتها الخادعة تقعُ بعض ميزات لغةٍ قويّة، أحدُها أنَّها الآن تُستَخدم من قِبَل عددٍ لا يُصدّق من التطبيقاتِ عاليةِ المستوى، ويُبيِّن ذلك أنَّ معرفةً أعمق بهذه التكنولوجيا إنّما هي مهارة مُهمّة لأي مُطوّر لشبكة الإنترنت أو المحمول. من المُفيد أن نبدأ بفكرةٍ عامَّة عن تاريخ هذه اللُّغة. تم إنشاء JavaScript في عام 1995 من قبل Brendan Eich، وهو مهندس في Netscape، وصدرت أولًا مع Netscape 2 في عام 1996. في الأصل كان مُتّفقٌ تسميتها LiveScript، ولكن تمَّ تغيير اسمها في قرارٍ تسويقيٍّ مشؤوم كمُحاولة للاستفادة من شعبية لُغة Java - التابعة لـ Sun Microsystems- على الرغم من أنَّ هُناك عدد قليل جدًا من القواسم المُشتركة بين اللغتين. لقد كان هذا مصدرًا للارتباك منذ ذلك الحين. بعد ثلاثةِ أشهرٍ أطلقت Microsoft نُسخة مُعظمُها مُتوافق مع اللُّغة أسمتها JScript مع IE . قدّمت Netscape اللُّغة إلى Ecma International، وهي مُنظّمةُ معاييرٍ أوروبيّة، وأسفرَ ذلك عن الطبعةِ الأولى من ECMAScript القياسيّة في عام 1997. تلقّت اللُغة عملية تحديث كبيرة كما في ECMAScript Edition 3 في عام 1999، وبَقيتْ مُستقرّة إلى حدٍ كبير منذ ذلك الحين. تمّ التخلي عن الطبعة الرابعة بسبب الخلافات السياسيّة بشأن تعقيد اللُّغة. شكَّلَتْ أجزاءٌ كثيرة من الطبعة الرابعة أساسَ الطبعة ECMAScript 5، التي نُشِرَت في ديسمبر من عام 2009 والطبعة الكبرى 6 والتي سيتمّ نشرها في عام 2015. من أجل الاعتياد، سوف أستخدمُ مُصطلحَ JavaScript طوال الوقت. خلافًا لمُعظم لغاتِ البرمجة، ليس لدى لغة JavaScript مفهوم الإدخال أو الإخراج. إنّما هي مُصمَّمةٌ لتعمل كلغةِ برمجةٍ نصيّة في بيئة استضافة، والأمرُ متروكٌ للبيئة المضيفة لتوفير آليات للتواصل مع العالم الخارجي. بيئةُ الاستضافةِ الأكثرِ شيوعًا هي المُتصفِّح، ولكن بالإمكان أيضًا العثور على مُفسّر JavaScript في Adobe Acrobat, Photoshop, SVG images, Yahoo!'s Widget engine، فضلًا عن البيئاتِ المُتاحةِ من جانب الخادوم مثل node.js. إلا أن قائمة المناطق حيث يتم استخدام JavaScript فقط تبدأ هُنا. تشتملُ اللُّغة أيضًا على قواعد بيانات NoSQL، مثل Apache CouchDB مفتوح المصدر، أجهزة الحاسوب المضمّنة أو بيئات سطح المكتب كاملة، مثل GNOME (واحدة من واجهات المستخدم الرسوميّة الأكثر شعبيّة لأنظمة التشغيل جنو / لينكس). نظرة عامةJavaScript هي لغة ديناميكيّة كائنيّة المنحنى. لديها أنواع types ومُشغِّلين operators، كائنات objects قياسيّة مُدمجة ووظائف methods. يأتي بناءُ الجُملةِ في JavaScript من لُغَتَي Java و C، وتنطبقُ العديدُ من الهياكل في تلك اللغات في JavaScript كذلك. إلَّا أنَّ أحد الفروق الرئيسيَّة هو أنَّه لا توجد فئات Classes في JavaScript. بدلًا من ذلك، يتم إنجاز وظائف الفئة عن طريق نماذج الكائن object prototypes. الفرقُ الرئيسي الآخر هو أن الدوال functions هي كائنات، تُعطى الدوال القدرة على الاحتفاظ بتعليماتٍ برمجيَّة قابلة للتنفيذ وتمريرها مثلها مثل أي كائن آخر. دعونا نبدأ من خلال النَّظر داخل لبنة بناء أي لغة: الأنواع. تقوم برامج JavaScript بمُعالجة القيم، وتنتمي كل تلك القيم إلى نوع. أنواع JavaScript هي: عدد Numberسلسلة Stringمنطقيَّة Booleanدالّة Functionكائن Objectرمز Symbol (جديد في الإصدار 6)كذلك undefined وnull، ويُعتبر هذين النوعين غريبين بعض الشيء. وهُناكَ أيضًا المصفوفة Array، والتي هي نوع خاص من الكائن. والتاريخ Date والمُنشئ RegExp، وهي كائناتٌ تحصلُ عليها مجانًا. ولكي نكون دقيقين من الناحية الفنيّة، الدوال ما هي إلَّا نوعٌ خاصٌّ من الكائن. ولذلك فإن الرسم البياني للنوع يبدو أكثر مثل ما يلي: عدد Numberسلسلة Stringمنطقيَّة Booleanرمز Symbol (جديد في الإصدار 6)كائن Objectدالّة Functionمصفوفة Arrayتاريخ Dateمُنشئ RegExpلا شيء Nullغير مُحدَّد Undefinedكذلك هُناك بعض أنواع الخطأ Error المُدمجة. لكن على كلِّ حال الأمور أسهل كثيرًا إذا اعتمدنا على الرسم البياني الأوَّل. الأرقامالأرقام في JavaScript هي "قِيَم IEEE 754 الدقيقة المُزدوجة في شكل 64-بِت"، وفقًا للمواصفات. لهذا المعنى بعض العواقب المُثيرة للاهتمام. ليس هُناكَ شيءٌ في JavaScript يُشبه العدد الصحيح integer، لذلك عليك أن تكون حذرًا قليلًا مع عمليّاتك الحسابيّة إذا كنتَ معتادًا على الرياضيات في C أو Java. احترس من أشياء مثل: 0.1 + 0.2 == 0.30000000000000004في المُمَارسة العمليّة، يتمّ التعامل مع القيم كما أعداد صحيحة من 32 بِت bit (ويتم تخزينهم بتلك الطريقة في بعض تطبيقات المُتصفِّح)، والتي يمكن أن تكون هامّة للعمليّات التي تستخدم نوع بِت. العوامل الحسابيّة القياسيّة مدعومة، بما في ذلك الجمع والطرح ومُعامل الحساب modulus أو (remainder)، وهكذا دواليك. هناك أيضًا كائن مُدمَج نسيتُ ذِكرَهُ سلفًا يُسمّى Math والذي يُمكنكَ استخدامَهُ إذا كنت ترغب في أداءِ دوالٍّ وثوابت رياضيّة أكثر تقدمًا: Math.sin(3.5); var d = Math.PI * r * r;يُمكنكَ تحويل سلسلة إلى عدد صحيح باستخدام الدالّة المُدمجة ()parseInt. هذه الدالّة تأخذ قاعدة للتحويل كمُعامِل argument ثاني اختياري، والتي ينبغي عليكَ توفيرها دائمًا: parseInt("123", 10); // 123 parseInt("010", 10); // 10رُبَّما تحصُل على نتائج مُذهلة في المُتصفِّحات القديمة (قبل عام 2013) إذا لم توفِّر قاعدة التحويل: parseInt("010"); // 8حَدَثَ ذلك لأن دالّة ()parseInt قرّرت مُعامَلة السلسلة كثُمانيّة octal نظرًا لأن 0 هي الرَّائدة. إذا كُنتَ ترغب في تحويل رقم ثنائيّ binary إلى عدد صحيح integer، فقط يتمّ تغيير القاعدة: parseInt("11", 2); // 3وبالمثل، يمكن تحليل أرقام الفاصِلة العائمة floating point numbers باستخدام الدالّة المُدمجة ()parseFloat والتي تَستَخدِم base 10 دائمًا خلافًا لرفيقتها ()parseInt. يُمكنكَ أيضًا استخدام عامل التشغيل الأحاديّ + لتحويل القيم إلى أرقام: + "42"; // 42يتمّ إرجاع قيمة خاصّة تُسمّى NaN (اختصار لـ”Not a Number”) إذا كانت السلسلة غير رقميّة: parseInt("hello", 10); // NaNNaN سامّة: إذا قُمتَ بتقديمها كمدخل input إلى أي عمليَّة حسابيَّة فإن النتيجة ستكون أيضًا NaN: NaN + 5; // NaNيُمكنكَ اختبار NaN باستخدام الدالّة المُدمجة ()isNaN: isNaN(NaN); // trueلدى JavaScript القيم الخاصّة Infinity و-Infinity: 1 / 0; // Infinity -1 / 0; // -Infinityيُمكنكَ اختبار قيم Infinity و–Infinity وNaN باستخدام الدالّة المُدمجة ()isFinite: isFinite(1/0); // false isFinite(-Infinity); // false isFinite(NaN); // falseمُلاحظة: تقوم دالَّتَي ()parseInt و()parseFloat بتحليل سلسلة حتى تصلا إلى حرفٍ غير صالح لشكل الرَّقم المُحدَّد، بعد ذلك يتمّ إرجاع الرقم الذي تم تحليله إلى تلك النقطة. رغم ذلك فإن المُعامِل "+" يقوم ببساطة بتحويل السلسلة إلى NaN إذا كان هُناك أيّ حرفٍ غير صالح في السلسلة. جرّب محاولة تحليل السلسلة "10.2abc" باستخدام كل طريقة مع نفسك في وحدة التَّحكُّم console، وسوف تفهم الاختلافات بشكلٍ أفضل. السلاسلالسلاسل في JavaScript هي مُتتابعات من الأحرف. ولتعريفٍ أكثر دقة، هي مُتتابعات من أحرف Unicode، يتمّ تمثيل كُل حرف في عدد 16 بِت. لا بُدَّ أنَّ هذا نبأ سار لأي شخص قد تعامل مع نظام التدويل. إذا كُنتَ ترغب في تمثيل حرف واحد، عليكَ استخدام سلسلة من طول 1 فقط. للعثور على طول سلسلة، قٌم باستدعاء خاصيّتها length: "hello".length; // 5هذه أول فُرشاة لدينا مع كائنات JavaScript! هل ذكرتُ لكَ أنَّ بامكانكَ استخدام السلاسل مثل الكائنات أيضًا؟ لديهم وظائف تسمحُ لكَ بمُعالجة السلسلة والوصول إلى معلومات عنها: "hello".charAt(0); // "h" "hello, world".replace("hello", "goodbye"); // "goodbye, world" "hello".toUpperCase(); // "HELLO"أنواع أخرىتُميّز JavaScript ما بين null، وهي القيمة التي تدل على لا شيء (ويمكن الوصول إليها من خلال طريقة واحدة ألا وهي استخدام الكلمة المحجوزة null)، وundefined، وهي قيمة من نوع 'غير معروف' التي تُشير إلى قيمة غير مُهيَّأة - أي قيمة لم يتمّ تعيينها بعد. سوف نتحدَّث عن المُتغيّرات في وقتٍ لاحق، ولكن يُمكن تعريف مُتغيّر في JavaScript دون تحديد قيمة له. إذا قمتَ بذلك، فإنَّ نوع المُتغيّر يُصبِح undefined. في الواقع نوع undefined ثابت. لدى JavaScript نوع Boolean مع القيم الممكنة true وfalse (وكلاهما كلمات محجوزة). يمكن تحويل أيّ قيمة إلى قيمة منطقيّة وفقًا للقواعد التالية: 1- تُصبِح كل من (false، 0، سلسلة فارغة (" ")، NaN، null وundefined) false. 2- تُصبِح كل القيم الأخرى true. يُمكنكَ إجراء هذا التحويل صراحة باستخدام دالَّة ()Boolean: Boolean(""); // false Boolean(234); // trueولكن نادرًا ما يكون هذا ضروريًّا، حيث أن JavaScript ستؤدّي بصمتٍ هذا التحويل عندما تتوقَّع قيمة منطقيَّة، مثل في عبارة if (انظر أدناه). لهذا السبب، ببساطة نذكُر أحيانًا "القيم الحقيقيّة" و"القيم الزائفة" قاصدين القيم التي تُصبح true وfalse على التوالي عند تحويلها إلى القيم المنطقيّة. بدلًا من ذلك يُمكن تسمية هذه القيم "truthy" و"falsy" على التوالي. العمليّات المنطقيّة مثل &&، logical and) || (logical or و! (logical not) مُعتمَدة. انظر أدناه. المُتغيّراتيتمّ تعريف المُتغيّرات الجديدة في JavaScript باستخدام var: var a; var name = "simon";إذا قمتً بتعريف مُتغيّر دون تعيين أيّ قيمة له، فإن نوعه يُصبِح undefined. هناك فارق هام عن لغات أخرى مثل Java ألا وهو أنّ الكُتَل blocks في JavaScript لا تملك نطاق scope؛ فقط الدوال هي التي لها نطاق. لذلك إذا تم تعريف مُتغيّر باستخدام var في جملة مُجمّعة (على سبيل المثال داخل if control structure)، سيكون المُتغيِّر مرئي من قِبَل الدالّة كاملة. إلَّا أنَّه بدءًا من ECMAScript الطبعة 6، تَسمحُ لك تعريفات let وconst امكانيَّة إنشاء مُتغيّرات block-scoped. المُعامِلاتمُعامِلات JavaScript الرقميّة هي +، -، *، / و٪ - وهو عامل ما تبقى. يتمّ تعيين القيم باستخدام = وهناك أيضًا جُمل تعيين مُركبّة مهمّة مثل += و-=. هذه تمتد إلى x = x مُعامِل y. x += 5 x = x + 5يُمكنكَ استخدام ++ و-- للزيادة والإنقاص على التوالي. كذلك بإمكانك أن تَستَخدِم هذه المُعامِلات كمُعامِلات prefix أو postfix. يقومُ المُعامِل + بربط السلسلة: "hello" + " world"; // "hello world"إذا قمتَ بإضافة سلسلة لعدد (أو قيمة أخرى) يتمّ تحويل كل شيء إلى سلسلة أولًا. يُمكن لما يلي أن يوضِّح المقصود: "3" + 4 + 5; // "345" 3 + 4 + "5"; // "75"إضافة سلسلة فارغة إلى شيء هي وسيلة مفيدة لتحويله إلى سلسلة. يُمكن إجراء المُقارنات في JavaScript باستخدام <، >، <= و =>. تَعمَلُ هذه المُقارنات مع السلاسل والأرقام على حدٍّ سواء. تبدو المُساواة أوضح قليلًا عن نظيراتها. تقوم المُساواة المزدوجة == بإجبار تغيير النَّوع إذا قُمتَ باعطائها أنواع مختلفة، وتُعطي نتائج مُثيرة للاهتمام في بعض الأحيان: "dog" == "dog"; // true 1 == true; // trueلتجنُّب إجبار النَّوع، استخدم المساواة الثلاثيَّة: 1 === true; // false true === true; // trueهُناكَ أيضًا != و!==. لدى JavaScript أيضًا مُعامِلات أُحاديّة. إذا كنت ترغب في استخدامها. هياكل التَّحكُّملدى JavaScript مجموعة مُماثِلة لهياكل التَّحكُّم الموجودة في لغات أخرى مثل عائلة C. العبارات الشرطيَّة مدعومة بواسطة if وelse؛ يُمكنكَ سَلسَلَتهم معًا إذا أردت: var name = "kittens"; if (name == "puppies") { name += "!"; } else if (name == "kittens") { name += "!!"; } else { name = "!" + name; } name == "kittens!!"لدى JavaScript حلقات while وحلقات do-while. الأولى جيِّدة لعمليات الحلقات البسيطة؛ والثانية مع تلك الحلقات إذا كنت ترغب في التأكُّد من أن جسد الحلقة يتمّ تنفيذه مرة واحدة على الأقل: while (true) { // an infinite loop! } var input; do { input = get_input(); } while (inputIsNotValid(input))For loop في JavaScript هي نفسها التي في C وJava: فهي تُتيح لكَ توفير معلومات التَّحكُّم للحلقة الخاصّة بك في سطرِ واحد. for (var i = 0; i < 5; i++) { // Will execute 5 times }يَستخدم المُعامِلين && و|| دائرة منطقيَّة قصيرة، وهو ما يعني أنَّها تعتمد على مُعامِلها الأوّل في إذا ما كانت ستُنفِّذ مُعامِلها الثاني. وهذا مُفيد لفحص الكائنات الفارغة قبل محاولة الوصول إلى سماتها: var name = o && o.getName();أو لوضع قيم افتراضيّة: var name = otherName || "default";لدى JavaScript مُعامِل ثُلاثي للتعبيرات الشرطيَّة: var allowed = (age > 18) ? "yes" : "no";يُمكن استخدام جُملة switch مع الفروع المتعدِّدة استنادًا إلى عددٍ أو سلسلة: switch(action) { case 'draw': drawIt(); break; case 'eat': eatIt(); break; default: doNothing(); }إذا لم تقم بإضافة break سوف يتوقّف التنفيذ عن المستوى التالي. وهذا نادرًا جدًا ما تُريد أن يحدث - في واقع الأمر هي تستحقّ وصفها على وجه التحديد fallthrough في تعليق إذا كُنتَ حقَا من المفترض أن تساعد في التصحيح: switch(a) { case 1: // fallthrough case 2: eatIt(); break; default: doNothing(); }يُعتبر شرط default اختياري. يُمكنكَ الحااق تعبيرات في كل من جزء switch وcase إذا أردت ذلك. تُجرَي المُقارنات بين اثنين باستخدام ===: switch(1 + 3) { case 2 + 2: yay(); break; default: neverhappens(); }الكائناتيُمكن اعتبار كائنات JavaScript كمجموعات بسيطة من أزواج name-value. على هذا النحو يمكن تشبيهها بـ: * القواميس Dictionaries في لغة Python * التجزئات Hashes في لغتي Perl وRuby * جداول التجزئة Hash Tables في لغتي C و C++ * HashMaps في لغة Java * مصفوفات الترابط Associative arrays في لغة PHP حقيقة أنّ هياكل البيانات تُستخدم في نطاق واسع ما هو إلَّا دليلٌ على تنوُّعها. وحيثُ أنَّ كل شيء (أنواع الشريط الأساسيَّة bar core types) في JavaScript هو كائن، فإنَّ أيّ برنامج JavaScript يحتوي بطبيعة الحال على قدرٍ كبيرٍ من عمليَّات البحث في جدول التجزئة. وهذا شيءٌ جيِّد والعمليَّات سريعة جدًا! جزء الـ"اسم" هو سلسلة JavaScript، في حين أنَّ القيمة يُمكن أن تكون أي قيمة لـ JavaScript - بما في ذلك المزيد من الكائنات. يَسمحُ هذا لك بامكانيَّة بناء هياكل بيانات من نوع التعقيد التعسُّفي. هُناكَ طريقتان أساسيَّتان لإنشاء كائن فارغ: var obj = new Object();و: var obj = {};و: function Person(name, age) { this.name = name; this.age = age; } // Define an object var You = new Person("You", 24); // We are creating a new person named "You" // (that was the first parameter, and the age..)هذه الطُرق مُتعادِلة لغويًّا؛ يُطلق على الثانية جُملة الكائن الحرفيّة، وهي الأكثر ملاءمة. بناء الجملة هذه هو أيضًا جوهر شكل JSON لذلك ينبغي تفضيل استخدامها في جميع الأوقات. يُمكن الوصول إلى سِمَات الكائن بمُجرّد إنشائه بطريقة من اثنتين: obj.name = "Simon"; var name = obj.name;و: obj["name"] = "Simon"; var name = obj["name"];هاتين الطريقتين مُتعادِلتين لُغويًا أيضًا. للطريقة الثانية ميزة ألا وهي أنَّ اسم الصفة المُميّزة property يتم توفيره كسلسلة، مما يعني أنَّه يُمكن حسابها في وقت التشغيل. رغم ذلك استخدام هذا الأسلوب يمنع بعض تحسينات مُحرِّك ومُصغِّر حجم JavaScript من التطبيق. أيضًا لتحديد set والحصول على get يُمكِنُكَ استخدام الخصائص ذات الأسماء المُتعارَف على أنَّها كلمات محجوزة: obj.for = "Simon"; // Syntax error, because 'for' is a reserved word obj["for"] = "Simon"; // works fineمُلاحظة: ابتداءً من EcmaScript 5، يُمكن استخدام الكلمات المحجوزة كأسماء خاصيّة الكائن المُميّزة object property مُجرّدة، وهذا يُعني أنَّها لا تحتاج إلى أن يتمّ وضعها بين اقتباسات عند تحديد حرفيَّات الكائن. راجع ES5 في Spec. يُمكن استخدام جُملة الكائن الحرفيّة لتهيئة كائن في مُجمَله: var obj = { name: "Carrot", "for": "Max", details: { color: "orange", size: 12 } }يُمكن سَلسَلة الوصول إلى الخاصيّة المُميّزة معًا: obj.details.color; // orange obj["details"]["size"]; // 12المصفوفاتفي الواقع المصفوفات في JavaScript هي نوعٌ خاصٌّ من الكائن. تعملُ المصفوفات كثيرًا مثل الأشياء العاديّة (يُمكن الوصول إلى الخصائص العدديّة باستخدام صياغة [] فقط) ولكن لدى المصفوفات خاصيَّة سحريَّة تُسمَّى ‘length’. هذه الخاصيَّة دائمًا ما تَزِيد بواحد عن أعلى مؤشِّر في المصفوفة. هُناك طريقة واحدة لخلق المصفوفات وهي على النحو التالي: var a = new Array(); a[0] = "dog"; a[1] = "cat"; a[2] = "hen"; a.length; // 3هُناك تأشير أكثر ملاءمة ألا وهو استخدام المصفوفات الحرفيَّة: var a = ["dog", "cat", "hen"]; a.length; // 3لاحظ أن array.length ليس بالضرورة عدد العناصر في المصفوفة. مع مراعاة ما يلي: var a = ["dog", "cat", "hen"]; a[100] = "fox"; a.length; // 101تذكَّر - طول المصفوفة يزيد بواحد عن أعلى مؤشِّر بها. إذا استعلمتَ عن مؤشِّر مصفوفة غير موجود، تحصل على نوع غير مُعرَّف undefined: typeof a[90]; // undefinedإذا أخذتَ ما سبق في الاعتبار، يمكنك التكرار عبر مصفوفة باستخدام ما يلي: for (var i = 0; i < a.length; i++) { // Do something with a[i] }هذه الطريقة غير فعّالة بعض الشيء حيثُ أنّكَ تقوم بالبحث عن الخاصيَّة length مرّة واحدة كل حلقة. تحسينٌ لهذا ما يلي: for (var i = 0, len = a.length; i < len; i++) { // Do something with a[i] }هذا أجمل في المظهر ولكن ذو لغة محدودة: for (var i = 0, item; item = a[i++];) { // Do something with item }نقوم هُنا بإعداد مُتغيّرين. يتم اختبار التعيين أيضًا في الجزء الأوسط من الحلقة للتأكُّد من المصداقيّة - إذا نَجَحَت، ستستمرّ الحلقة في العمل. وحيثُ أن i يتزايد في كل مرّة، سيتمّ تعيين العناصر من المصفوفة إلى عناصر في ترتيب تسلسلي. تتوقّف الحلقة عندما يتمّ العثور على قيم "falsy" (مثل نوع undefined). يجبُ عليكَ استخدام هذه الحيلة فقط مع المصفوفات التي تَعْلَمُ أنَّها لا تحتوي على قِيَم "falsy" (على سبيل المثال مصفوفات من كائنات أو نموذج كائن المُستند DOM). إذا كُنتَ تقوم بالتكرار خلال بيانات رقميَّة يُمكن أن تشمل 0 أو بيانات سلسلة يُمكن أن تشمل سلسلة فارغة يجبُ عليكَ بدلًا من ذلك استخدام تعبير i, len. هُناك طريقة أخرى للتكرار وهي استخدام حلقة for…in. لاحظ أنَّهُ إذا قامَ شخصٌ بإضافة خصائص جديدة لـ Array.prototype فإنَّه سيتم تكرارهم من قِبَل هذه الحلقة أيضًا: for (var i in a) { // Do something with a[i] }إذا كُنتَ ترغب في إلحاق عنصر إلى مصفوفة، ببساطة قُم بذلك كما يلي: a.push(item);تأتي المصفوفات مع عدد من الوظائف. انظر أيضًا وثائق كاملة عن أساليب المصفوفة. الوصفاسم الدالَّةتقوم بإرجاع سلسلة مع toString() من كل عنصر مفصولة بفواصل.a.toString()تقوم بإرجاع سلسلة مع toLocaleString() من كل عنصر مفصولة بفواصل.a.toLocaleString()تقوم بإرجاع مصفوفة جديدة مع عناصر مضافة إليها.a.concat(item1[, item2[, ...[, itemN]]])تقوم بتحويل المصفوفة إلى سلسلة - قيم محدَّدة من قبل العامل المُتغيِّر sepa.join(sep)تقوم بإزلة وإرجاع العنصر الأخير.a.pop()تقوم push بإضافة عنصر واحد أو أكثر في النهاية.a.push(item1, ..., itemN)تقوم بعكس المصفوفة.a.reverse()تقوم بإزالة وإعادة العنصر الأول.a.shift()تقوم بإرجاع مصفوفة فرعيَّة (Sub-array).a.slice(start, end)تقوم بأخذ دالَّة مقارنة اختياريَّة.a.sort([cmpfn])تتيح لك تعديل مصفوفة عن طريق حذف قسم واستبداله بمزيد من العناصر.a.splice(start, delcount[, item1[, ...[, itemN]]])تقوم بإلحاق عناصر إلى بداية المصفوفة.a.unshift([item]) الدوالّالدوالّ هي العنصر الأساسيّ في فهم JavaScript جنبًا إلى جنبٍ مع الكائنات. لا يُمكن للدالّة المُجرّدة أن تكون أسهل من التالية: function add(x, y) { var total = x + y; return total; }تدلُّ التعليمات البرمجيَّة هذه على كل شيء يُمكن معرفته عن الدوال الأساسية. يُمكن لدالّة JavaScript أن تستقبل 0 أو أكثر مما تُدعى مُتغيّرات. يُمكن لجسم الدالّة أن يحتوي على عدد ما تُريد من الجُمل statements، ويُمكن أن تُعرّف مُتغيّراتُها الخاصّة والتي تكون محليّة بالنسبة لتلك الدالّة. يُمكن استخدام جُملة return لإرجاع قيمة في أي وقت ثُمَّ إنهاء الدالّة. إذا لم يتمّ استخدام أيّ جُملة إرجاع (أو تمّ استخدامها وكانت فارغة بدون قيمة) فإن JavaScript تقوم بإرجاع نوع 'غير مُعرَّف'. تُعتبر المُتغيّرات المُسمّاه كمبادئ توجيهيّة أكثر من أي شيء آخر. يُمكنكَ استدعاء دالّة دون تمرير المُتغيّر الذي تتوقّعه الدالّة، في هذه الحالة سيتمّ تعيين المُتغيّر كنوع غير مُعرَّف. add(); // NaN // You can't perform addition on undefinedيُمكنكَ أيضًا تمرير عدد من المُتغيّرات أكثر من العدد الذي تتوقّعه الدالّة: add(2, 3, 4); // 5 // added the first two; 4 was ignoredقد يبدو هذا سخيفًا بعض الشيء ولكن لدى الدوال خاصيَّة الوصول إلى مُتغيّرات إضافيّة داخل أجسامهم تُسمّى arguments، والتي هي كائن مثل المصفوفة تحتوي على كافّة القيم التي تمَّ تمريرُها إلى الدالّة. دعونا نُعيد كتابة دالّة الجمع لتأخذ عدد ما نُريد من القيم: function add() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[i]; } return sum; } add(2, 3, 4, 5); // 14رغم ذلك فإنَّ هذا في الحقيقة ليس له أيّ فائدة على مُجرَّد كتابة 2 + 3 + 4 + 5. دعونا نقوم بإنشاء دالّة المتوسِّط: function avg() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[i]; } return sum / arguments.length; } avg(2, 3, 4, 5); // 3.5هذا مفيدٌ جدًا ولكنَّه يُظهِر مُشكلة جديدة. تأخذ دالّة ()avg قائمة من المُتغيّرات مفصولٌ بينَ كلٍّ منها بفاصِلة - ولكن ماذا لو كُنتَ تُريد أن تجد مُتوسّط مصفوفة؟ يُمكنك كتابة الدالّة على النحو التالي: function avgArray(arr) { var sum = 0; for (var i = 0, j = arr.length; i < j; i++) { sum += arr[i]; } return sum / arr.length; } avgArray([2, 3, 4, 5]); // 3.5سيكونُ من الجميل أن يُصبح بمقدورِنا إعادة استخدام الدالَّة التي قُمنا بإنشائِها مُسبقًا. لحُسن الحظّ، تُمكنُّكَ Javascript من استدعاء دالّة مع مصفوفة من قيم المُتغيّرات العشوائيّة، وذلكَ باستخدام وظيفة apply() من أي كائن دالّة. avg.apply(null, [2, 3, 4, 5]); // 3.5قيمة المُتغيّر الثانية لـ ()apply هي المصفوفة المُستخدمة كقيم المُتغيّرات؛ سيتم نقاش المُتغيّر الأول في وقت لاحق. هذا يؤكد حقيقة أن الدوالّ هي كائنات أيضًا. تُتيح لكَ Javascript إنشاء وظائف مجهولة. var avg = function() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[i]; } return sum / arguments.length; }هذا هو ما يُعادل لُغويًا نموذج دالّة ()avg. إنَّها قويَّة للغاية حيثُ أنها تُتيحُ لكَ وضع تعريف دالَّة كاملة في أي مكان يُمكنكَ عادة وضع تعبير expression به. يُتيحُ هذا كل أنواع الحِيَل. إليكَ طريقة لـ "اخفاء" بعض المُتغيّرات المحليّة – مثل نطاق الكتلة block scope في لغة C: var a = 1; var b = 2; (function() { var b = 3; a += b; })(); a; // 4 b; // 2تسمحُ لكَ JavaScript استدعاء دوال بشكلٍ متكرِّر. هذا الأمرُ مفيدٌ بشكلِ خاصّ في التعامل مع هياكل الشجرة tree structures، مثل ما تحصُلُ عليه في نموذج كائن المُستند DOM بالمُتصفّح. function countChars(elm) { if (elm.nodeType == 3) { // TEXT_NODE return elm.nodeValue.length; } var count = 0; for (var i = 0, child; child = elm.childNodes[i]; i++) { count += countChars(child); } return count; }هذا يُسلّط الضَوْء على مشكلةٍ مُحتملةٍ مع الدوال المجهولة: كيف يُمكن استدعاؤها بشكلِ متكرر إذا لم يكُن لديها اسم؟ تُتيحُ لكَ JavaScript تسمية تعبيرات دالَّة لهذا الغرض. يُمكنكَ استخدام تعبيرات تنفيذ الدالَّة مُباشرةً (IIFEs - Immediately Invoked Function Expressions) المُسمّاة على النحو التالي: var charsInBody = (function counter(elm) { if (elm.nodeType == 3) { // TEXT_NODE return elm.nodeValue.length; } var count = 0; for (var i = 0, child; child = elm.childNodes[i]; i++) { count += counter(child); } return count; })(document.body);الاسم المُقدّم إلى تعبير دالّة على النحو الوارد أعلاه إنَّما هو متاحٌ فقط لنطاقِ الدالّة نفسها. يَسمحُ هذا بمزيدِ من التحسينات تُنفَّذ من قِبَل المُحرِّك وتعليمات برمجيَّة أكثرُ قابليَّة للقراءة على حدٍّ سواء. يظهر الاسم أيضًا في المُصَحِّح debugger وبعض stack trace التي يمكن أن تُوفِّر لكَ الوقت. لاحظ أن دوالّ JavaScript هي نفسها كائنات ويُمكنكَ إضافة أو تغيير خصائص بها تمامًا مثل الكائنات الّتي تطرَّقنا إليها في جُزء الكائنات. الكائنات المُخصَّصَةمُلاحظة: للاطّلاع على نقاش أكثر تفصيلًا عن البرمجة الشيئيَّة في JavaScript، انظر مُقدِّمة إلى JavaScript الشيئيّة. في البرمجة الشيئيّة الكلاسيكيّة، الكائنات هي مجموعات من بيانات ووظائف تعملُ على تلك البيانات. JavaScript هي لغة النموذج القائم والتي لا تحتوي على فئات مثل تِلكَ التي توجد في C++ أو Java. (هذا الامرُ مُربِك أحيانًا للمبرمجين مِمَّن اعتادوا على لغات تحتوي على فئات.) بدلًا من ذلك تَستَخدِم JavaScript الدوال كفئات. لنعتبر أن هُناك كائن عبارة عن شخص Person لديه حقلي الاسم الأول والاسم الأخير. هُناك نوعان من الطرق التي من خلالها قد يتمّ عرض الاسم: كما "الأوَّل الأخير" أو "الأخير، الأوَّل". هذه طريقة لفعل ذلك باستخدام الدوال والكائنات التي ناقشناها سابقًا: function makePerson(first, last) { return { first: first, last: last }; } function personFullName(person) { return person.first + ' ' + person.last; } function personFullNameReversed(person) { return person.last + ', ' + person.first; } s = makePerson("Simon", "Willison"); personFullName(s); // "Simon Willison" personFullNameReversed(s); "Willison, Simon"هذه الطريقة تعمل ولكنَّها سيِّئة جدًا. ينتهي بكَ الأمر مع العشرات من الدوال في مساحتك العامَّة global namespace. ما نحتاجه حقًا هو وسيلة لإرفاق دالَّة بكائن. حيثُ أنَّ الدوال هي كائنات، هذا أمر سهل: function makePerson(first, last) { return { first: first, last: last, fullName: function() { return this.first + ' ' + this.last; }, fullNameReversed: function() { return this.last + ', ' + this.first; } }; } s = makePerson("Simon", "Willison") s.fullName(); // "Simon Willison" s.fullNameReversed(); // "Willison, Simon"هُناك شيء في هذه الجُمل التعليميَّة لم نَرَ من قبل: الكلمة المحجوزة this. تُستَخدَم this داخل الدالَّة وتُشير إلى الكائن الحالي. ما يُعنيه ذلك بالتحديد أنه يتمّ التعيين نِتاجًا عن الطريقة التي قُمتَ باستدعاء الدالَّة بها. إذا قمت باستدعائها باستخدام تأشير النقطة أو تأشير القوس في كائن، يُصبِح هذا الكائن this. إذا لم يتمّ استخدام تأشير النقطة dot notation للاستدعاء، تُشير this إلى الكائن العام global object. لاحظ أن this هي سبب مُتكرِّر للأخطاء. على سبيل المثال: s = makePerson("Simon", "Willison"); var fullName = s.fullName; fullName(); // undefined undefinedعندما نستدعي ()fullName وحدها، من دون استخدام ()s.fullName، فإنَّ هذا مُقيَّد بالنطاق العام global scope. وبما أنَّه لا توجد مُتغيِّرات عامَّة global variables تُسمَّى first أو last فإنَّنا نحصل على نوع undefined لكلِّ واحد منهما. يُمكننا الاستفادة من الكلمة المحجوزة this لتحسين دالَّة makePerson: function Person(first, last) { this.first = first; this.last = last; this.fullName = function() { return this.first + ' ' + this.last; }; this.fullNameReversed = function() { return this.last + ', ' + this.first; }; } var s = new Person("Simon", "Willison");قدَّمنا كلمة محجوزة أُخرى: new. ترتبط new ارتباطًا وثيقًا بـ this. ما تفعله هو أنَّها تخلُق كائن فارغ جديد تمامًا، ثم تدعو الدالَّة المُحدَّدة ويتمّ تعيين this لهذا الكائن الجديد. لاحظ أنَّ الدالَّة المُحدَّدة بواسطة this لا تُرجِع قيمة ولكنَّها فقط تقوم بتعديل كائن this. تُرجِع new كائن this إلى موقع الاستدعاء. يُطلَق على الدوال المُصمَّمة ليتمّ استدعاؤها بواسطة new دوال المُنشِئ. مُمارسة شائعة هي كتابة تلك الدوال بأحرف كبيرة كتذكير لاستدعائهم بواسطة new. لا يزال لدى الدالَّة المُحسَّنة نفس المأزق مع استدعاء ()fullName وحدها. بدأت كائنات Person تُصبِح أفضل ولكن لا تزال هُناك بعض الحوافّ السيِّئة لديها. في كل مرة نقوم بإنشاء كائن Person جديد فنحن نقوم بخلق كائني دالَّة جديدين به كذلك - ألن يكون أفضل لو كانت هذه التعليمات البرمجيَّة مُشتَرَكة؟ function personFullName() { return this.first + ' ' + this.last; } function personFullNameReversed() { return this.last + ', ' + this.first; } function Person(first, last) { this.first = first; this.last = last; this.fullName = personFullName; this.fullNameReversed = personFullNameReversed; }هذا أفضل: نقومُ بخلق دوال الوظيفة مرَّة واحدة فقط، ونُعيِّنُ مراجع لهم داخل المُنشئ. هل بإمكاننا فعل ما هو أفضل من ذلك؟ الجواب هو نعم: function Person(first, last) { this.first = first; this.last = last; } Person.prototype.fullName = function() { return this.first + ' ' + this.last; }; Person.prototype.fullNameReversed = function() { return this.last + ', ' + this.first; };Person.prototype هو كائن مُشتَرَك من قِبَل كافَّة نماذج Person. يُشكِّلُ هذا الكائن جُزءًا من سلسلة بحث (لها اسم خاص وهو "سلسلة النموذج" prototype chain): متى حاولتَ الوصول إلى خاصيَّة غير مُعيَّنة في Person، سوف تتحقَّق JavaScript من Person.prototype لترى ما إذا كانت هذه الخاصّيّة موجودة هناك بدلًا من ذلك. نتيجة لذلك، أي شيء مخصَّص لـ Person.prototype يُصبِح متاحًا لجميع النماذج من هذا المُنشئ عبر كائن this. هذه أداة قويَّة بشكلٍ لا يُصدَّق حيثُ أنَّ JavaScript تُتيحُ لكَ تعديل نموذج شيء في أيّ وقت في بَرنامجك، ممَّا يُعني أنَّه يُمكنكَ زيادة وظائف إضافيَّة لكائنات موجودة في وقت التَّشغيل: s = new Person("Simon", "Willison"); s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function Person.prototype.firstNameCaps = function() { return this.first.toUpperCase() }; s.firstNameCaps(); // "SIMON"من المُثير للاهتمام أنَّهُ يُمكنكَ أيضًا إضافة أشياء إلى النموذج الخاصّ بالكائنات المُدمجة في JavaScript. دعونا نُضيف وظيفة إلى سلسلة تقوم بإرجاع هذه السلسلة معكوسة: var s = "Simon"; s.reversed(); // TypeError on line 1: s.reversed is not a function String.prototype.reversed = function() { var r = ""; for (var i = this.length - 1; i >= 0; i--) { r += this[i]; } return r; }; s.reversed(); // nomiSتعمل الطَّريقة الجديدة حتَّى مع حرفيَّات السلسلة string literals "This can now be reversed".reversed(); // desrever eb won nac sihTكما ذكرتُ سالِفًا، يُشكِّل النموذج جزءًا من سلسلة. جذر تلك السلسلة هو Object.prototype والذي من وظائفه ()toString - تلك هي الوظيفة التي يتمّ استدعاؤها عندما تُحاول تمثيل كائن كسلسلة. يُعتبَر هذا مفيدًا من أجل تصحيح كائنات Person: var s = new Person("Simon", "Willison"); s; // [object Object] Person.prototype.toString = function() { return '<Person: ' + this.fullName() + '>'; } s; // "<Person: Simon Willison>"أتذكُرُ كيف احتَوَت ()avg.apply على مُعامِل أوَّل فارغ null؟ يُمكننا إعادة النَّظر في ذلك الآن. المُعامِل الأول في ()apply هو الكائن الذي يجب أن يُعامَل على أنَّهُ 'this'. هذا تنفيذٌ تافه لـ new على سبيل المثال: function trivialNew(constructor, ...args) { var o = {}; // Create an object constructor.apply(o, args); return o; }لا يُعتَبرُ هذا نُسخة طبق الأصل من new لأنها لم تقم بإعداد سلسلة النَّموذج (سيكون من الصَّعب التَّوضيح). هذا ليس شيئًا تستخدمه في كثيرٍ من الأحيان ولكن من المفيد أن تعرف عنه. يُطلَق على مُقتطف ...args (بما في ذلك علامات الحذف) "المُعامِلات المُتبقّية" rest arguments - كما يُوحِي اسمها فهي تحتوي على بقيَّة المُعامِلات. استدعاء var bill = trivialNew(Person, "William", "Orange");يُعادِل تقريبًا var bill = new Person("William", "Orange");لدى ()apply دالَّة أخت اسمها call، والتي أيضًا تُتيحُ لكَ تعيين this ولكنَّها تأخذ قائمة موسَّعة من المُعامِلات بدلًا من مصفوفة. function lastNameCaps() { return this.last.toUpperCase(); } var s = new Person("Simon", "Willison"); lastNameCaps.call(s); // Is the same as: s.lastNameCaps = lastNameCaps; s.lastNameCaps();الدوالّ الداخليّةتعريفات دالَّة JavaScript مسموحٌ بها داخل دوال أخُرى. لقد رأينا هذا من قبل مع دالَّة ()makePerson. هُناك تفصيل مهمّ في الدوال المُتداخلة في JavaScript وهو أنَّه يُمكِنُها الوصول إلى مُتغيِّرات في نِطَاق الدالَّة الأم: function betterExampleNeeded() { var a = 1; function oneMoreThanA() { return a + 1; } return oneMoreThanA(); }يوفِّر هذا قدرًا كبيرًا من الفائدة في كتابة تعليمات برمجيَّة أكثر صيانة. إذا اعتَمَدَتْ دالَّة على واحدة أو اثنتين من الدوال الأُخرى غير المُفيدة لأي جُزء من تعليماتك البرمجيَّة، يُمكنكَ إدخال تلك الدوال ذات المنفعة في الدالَّة التي سيتمّ استدعاؤها من مكان آخر. يُحافظ هذا على عدد من الدوال التي هي في النطاق العام، وهذا دائمًا شيءٌ جيد. يُعتبر هذا عدَّاد كبيرة لإغراء المُتغيِّرات العامَّة global variables. عند كتابة تعليمات برمجيَّة معقَّدة غالبًا ما يكون مُغريًا استخدام مُتغيِّرات عامَّة لتبادُل القيم بين دوالّ مُتعدِّدة - الأمر الذي يؤدِّي إلى تعليمات برمجيِّة من الصعب صيانتها. يُمكنُ للدوالّ المُتداخلة مُشاركة المُتغيِّرات الموجودة في الدالَّة الأم، لذلك يُمكنكَ استخدام هذه الآليَّة لتجميع دالَّتين معًا متى كان هذا منطقيَّا دون تلويث مساحتُكَ العامَّة – ‘محليّات عامَّة’ إذا أردتَ أن تُطلِق عليها ذلك. ينبغي استخدام هذا الأسلوب مع الحذر، إلَّا أنَّ حوذته قُدرة مُفيدة. الإغلاقيقودنا هذا إلى واحدة من التجريدات الأكثر قوّة التي بإمكان Javascript تقديمها - ولكن أيضًا الأكثر تعريضًا للارتباك. ماذا تفعل هذه التعليمات البرمجيَّة؟ function makeAdder(a) { return function(b) { return a + b; }; } var x = makeAdder(5); var y = makeAdder(20); x(6); // ? y(7); // ?يجب أن يكون اسم دالَّة makeAdder قد أوضَح كل شيء: يقوم بخلق دوال 'adder' جديدة، تلك الدوال التي عند استدعائها بواسطة مُعامِل تقوم باضافته إلى المُعامِل الذي تم إنشائهم بواسطته. ما يحدث هُنا هو إلى حدٍّ كبير نفس ما كان يحدث مع الدوال الداخليَّة في وقتٍ سابق: للدالَّة المُعرَّفة داخل دالَّة أخرى حقّ الوصول إلى مُتغيِّرات الدالَّة الخارجيَّة. الفرقُ الوحيدُ هُنا هو أن الدالَّة الخارجيَّة قد عادت، وبالتالي الحس السليم يهدي إلى أن مُتغيِّراتها المحليَّة لم تعد موجودة. لكنها لا تزال موجودة - لولاها دوال adder ما كانت قادرة على العمل. ما هو أكثر من ذلك، هناك نوعان من "نسخ" مختلفة من المُتغيِّرات المحليّة الخاصَّة بـ makeAdder - نُسخة بها a هو 5 ونُسخة بها a هو 20. وبُناءً على ذلك فإن نتيجة استدعاءَات الدالَّة تلك هي على النحو التالي: x(6); // returns 11 y(7); // returns 27إليكَ ما يحدُث فعليًّا. عندما تقوم Javascript بتنفيذ دالَّة يتمّ إنشاء كائن 'نطاق' للاحتفاظ بالمُتغيِّرات المحليَّة التي تم إنشاؤها داخل تلك الدالَّة. يتمّ تهيئتها مع أيّ مُتغيَّر يتمّ ارساله كعامِل دالَّة مُتغيِّر. يُشبه هذا الكائن العام الذي يحتوي على جميع المُتغيِّرات والدوال العامَّة، ولكن هُناك مع بعض الاختلافات الهامَّة: أوَّلُها، يتمَّ إنشاء كائن نطاق جديد تمامًا في كل مرة تبدأ دالَّة بالتنفيذ. ثانيها، لا يُمكن الوصول المُباشِر إلى كائنات النطاق تلك من خلال جُمل Javascript البرمجيَّة الخاصَّة بك، على عكس الكائن العام (الذي يُمكن الوصول إليه كـ this ويُمكن الوصول إليه في المُتصفِّحات كـwindow). على سبيل المثال ليس هُناك آليَّة لتكرار خصائص كائن النِطاق الحالي. لذلك عندما يتم استدعاء makeAdder، يتمّ إنشاء كائن نطاق مع خاصيَّة واحدة: a، والذي هو المُعامِل الذي تمّ تمريره إلى دالَّة makeAdder. تُعيد بعد ذلك makeAdder دالَّة تمّ إنشاؤها حديثًا. في هذه المرحلة عادةً يقوم جامع القُمامة في JavaScript بتنظيف كائن النطاق الذي تمّ خلقه لـ makeAdder، ولكن الدالًّة المُعادة تُحافظ على مرجع إلى كائن النطاق هذا. نتيجة لذلك، لن يكون كائن النطاق القمامة مُجمَّعة إلى أن لا يكون هُناك أيَّة مراجع إلى كائن الدالَّة التي أعادتها makeAdder. تُكوِّن كائنات النطاق سلسلة تُسمَّى سلسلة نطاق scope chain، على غرار سلسلة النَّموذج المُستخدمة من قبل نظام كائن JavaScript. الإغلاق هو مزيجٌ من دالَّة وكائن النِّطاق الذي بِهِ تمّ إنشاء تلك الدالَّة. يُمكِّنُكَ الإغلاق من حفظ الحالة - كما أنَّه كثيرًا ما يمكن استخدامه بدلًا من الكائنات. يُمكِن العثور على عدَّة مُقدِّمات ممتازة للإغلاق هُنا. تسريبات الذَّاكرةأحد الآثار الجانبيّة المؤسفة للإغلاق هو أنَّه يجعل من السَّهل تسرب الذاكرة في Internet Explorer. Javascript هي لغة تجميع البيانات المُهملة - تُخصَّص ذاكرة للكائنات عند إنشائها ويتم استعادة تلك الذَّاكرة من قِبَل المُتصفِّح عندما لا تتبقَّى مراجع إلى كائن. يتم التعامل مع الكائنات التي توفّرها البيئة المضيفة عن طريق تلك البيئة. تحتاج مُستضيفات المُتصفِّح إلى إدارة عدد كبير من الكائنات التي تمثل صفحة HTML المُقدَّمة ألا وهي كائنات الـ DOM. الأمر متروك للمُتصفِّح لإدارة تخصيص واسترداد هذه الكائنات. يستخدم Internet Explorer لهذا آلية جمع القمامة الخاصَّة به، بعيدًا عن الآليَّة المستخدمة لـ JavaScript. إنَّ التفاعل بين الآليَّتين هو ما قد يتسبِّب في تسرُّب الذَّاكرة. يحدث تسرُّب الذاكرة في IE أي وقت يتمّ تشكيل مرجع دائري بين كائن JavaScript وكائن أصلي. تأمَّل ما يلي: function leakMemory() { var el = document.getElementById('el'); var o = { 'el': el }; el.o = o; }تخلُق المراجع الدائريَّة المُشكَّلة بالأعلى تسرُّب للذَّاكرة. لن يقوم IE بتحرير الذَّاكرة المُستخدمة من قِبَل el وo حتَّى يتمّ إعادة تشغيل المُتصفِّح تمامًا. من المُرجَّح ألّا تُلاحَظ الحالة المذكورة أعلاه. يُصبح تسرُّب الذَّاكرة مصدر قلق حقيقي فقط في التطبيقات طويلة التشغيل أو التطبيقات التي تُسرِّب قدرًا كبيرًا من الذَّاكرة بسبب هياكل البيانات الكبيرة أو أنماط التسرُّب داخل الحلقات. نادرًا ما تكون التسريبات واضحة بهذا الشَّكل - في كثيرٍ من الأحيان يمكن أن يكون لهيكل البيانات المسرَّبة عدة طبقات من المراجع تقومُ بالعتيم على مرجع مُعاد. يجعلُ الإغلاق من السَّهل خلق تسرُّب ذاكرة دون قصد. انظر إلى التعليمات البرمجيَّة هذه: function addHandler() { var el = document.getElementById('el'); el.onclick = function() { el.style.backgroundColor = 'red'; }; }تقوم التعليمات البرمجيَّة أعلاه بتحويل العنصر إلى أحمر عند النقر عليه. كما أنَّها أيضًا تخلُق تسرُّب ذاكرة. لماذا؟ لأن الإشارة إلى el قُبِضَت دون قصد في الإغلاق الذي تمَّ إنشاؤه للدالَّة الداخليَّة المجهولة. يخلُق هذا مرجعًا دائريًّا بين كائن JavaScript (الدالَّة) وكائن أصلي (el). هُناك عدد من الحلول لهذه المشكلة. أبسطُها هو عدم استخدام مٌتغيِّر el: function addHandler(){ document.getElementById('el').onclick = function(){ this.style.backgroundColor = 'red'; }; }المُثير للدَّهشة أن هُناك خدعة لكسر المراجعِ الدائريَّة تُقدَّم بواسطة الإغلاق، هذه الخُدعة هي إضافةُ إغلاقٍ آخر: function addHandler() { var clickHandler = function() { this.style.backgroundColor = 'red'; }; (function() { var el = document.getElementById('el'); el.onclick = clickHandler; })(); }يتمُّ تنفيذ الدالّة الداخليّة على الفور وتُخفي محتوياتها عن الإغلاق الذي تمَّ انشاؤه باستخدام clickHandler. خدعة أُخرى جيّدة لتجنب الإغلاق هي كسر المراجعِ الدائريَّة خلال حدث window.onunload. سوف تقوم العديد من مكتبات الحدث بتنفيذِ ذلك بدلًا عنك. لاحظ عمل بذلك يقوم بتعطيل ذاكرة التخزين المؤقت back-forward في فيرفُكس، لذلك يجبُ عليكَ أن لا تُسجِّل مُستمع unload في فَيرفُكس، إلا إذا كان لديك أسباب أخرى للقيام بذلك.1 نقطة
-
تمّ تطوير Redis في عام 2009، وهو مُخزّن بيانات بنمط المفتاح والقيمة (key value)، وهو مفتوح المصدر، ويُقدّم مرونة في الاستخدام، وكما في جميع نمط قواعد البيانات من نوع NoSQL الّتي اتبعها Redis، أمثال Cassandra, CouchDB, MongoDB، فإن Redis يَسمح للمُستخدِم بتخزين كميات ضخمة من البيانات بدون التقيد المفروض بقواعد البيانات العلائقيّة/الارتباطيّة (relational database)، بالإضافة إلى أنّه قد تمّ مُشابهته ومقارنته مع memcache، فيُمكن استخدامه بعناصره الأساسية كنظام تخبئة. إعداد بيئة ومتطلبات Redis قبل الشروع في تنصيب redis، يجب توفّر بعض المُتطلّبات والّتي من شأنها أنّ تجعل من عمليّة التنصيب أكثر سهولة. سيتمّ في البداية تحديث جميع حزم apt-get: sudo apt-get update سيتمّ بعد ذلك تحميل مُترجم (compiler) باستخدام الحزمة build-essential، والّتي من شأنها المساعدة في تنصيب Redis من المصدر: sudo apt-get install build-essential سيتمّ في الخطوة الأخيرة تحميل الأداة tcl الّتي يَعتمد عليها Redis: sudo apt-get install tcl8.5 تنصيب Redis بعد أنّ تمّ تنصيب المُتطلّبات الأساسيّة، فمن المُمكن الآن الشروع وتنصيب redis، ويُمكن تحديد الإصدار المطلوب أو تحميل الإصدار الأخير والذي سيحمل دائمًا الاسم redis-stable: wget http://download.redis.io/redis-stable.tar.gz يجب بعد ذلك فك ضغط الملفّ والانتقال إليه: tar xvzf redis-stable.tar.gz cd redis-stable يجب الآن المُتابعة مع الأمر make: make ولتنصيب Redis على كامل النّظام، فيُمكن إما نسخ ملفاته من المصدر: sudo cp src/redis-server /usr/local/bin/ sudo cp src/redis-cli /usr/local/bin/ أو تنفيذ الأمر التّالي: sudo make install بعد انتهاء عمليّة التنصيب، من المُستحسن تشغيل Redis كحارس (daemon) في خلفيّة النّظام، ولعمل ذلك يأتي Redis بملفّ برمجي (سكريبت) لهذه المُهمّة. يجب الانتقال إلى المسار utils للوصول إلى هذا الملفّ: cd utils ومن ثم تشغيل الملفّ الخاص بتوزيعات Ubuntu/Debian: sudo ./install_server.sh سيَعرض السكريبت بعض الأسئلة لإتمام عمليّة التهيئة، ولكن يُمكن الاعتماد على الإعداد الافتراضي والاكتفاء بالضغط على Enter، وبعد انتهاء عملية التهيئة سيكون خادم Redis يعمل في الخلفيّة (background). يُمكن تنفيذ الأمر التّالي للوصول إلى قاعدة البيانات Redis: redis-cli يُمكن اختبار Redis كالتّالي: $ redis-cli redis 127.0.0.1:6379> ping PONG redis 127.0.0.1:6379> set mykey somevalue OK redis 127.0.0.1:6379> get mykey "somevalue" أوامر Redis يتمّ إضافة بعض المُعطيات إلى سلسلة رموز (string)، والّتي تُعتبر من أبسط أنواع البيانات (datatype)، باستخدام الأمر التّالي: > SET users:SomeOne "job: SomeJob, email:example@example.com, age: some number " OK تمّ في الأمر السابق استخدام الأمر SET متبوعًا بالمُفتاح users:SomeOne، ومن ثمّ القيمة (value)، وهي سلسلة الرموز (string) نفسها. لا تُؤثر النقطتان (:) الموجودة في المُفتاح على الأمر السابق، ولكن من المُمكن الاستفادة منهما في وصف أوضح للمُفتاح المُراد تعيينه. يُمكن استعراض تفاصيل سلسلة الرموز السابقة باستخدام الأمر GET: GET users:SomeOne "job: SomeJob, email:example@example.com, age: some number " المجالات (Ranges) يتمّ تحديد مجال باستخدام مُعاملين والذين سيُمثلان العنصر الأوّل والعنصر الأخير، فالعنصر الأوّل سيُعتبر 0، وإن كان المُعامل الأخير -1، فإن جميع العناصر حتّى نهاية القائمة سيتمّ شملها، فعلى سبيل المثال، إن كانت قائمة تحتوي على ألوان قوس قزح مُرتبة بالترتيب ROYGBV، فيُمكن استعراضها كما في التّالي: > LRANGE ROYGBV 0 3 1) "red" 2) "orange" 3) "yellow" 4) "green" > LRANGE ROYGBV 0 -1 1) "red" 2) "orange" 3) "yellow" 4) "green" 5) "blue" 6) "violet" > LRANGE ROYGBV 3 -1 1) "green" 2) "blue" 3) "violet" انتهاء الصلاحية (Expiration) لا يُعتبر Redis ذو فائدة في تخزين المعلومات فقط، بل يُمكن استخدامه أيضًا في إنهاء صلاحيتها. يُعيّن الوقت اللازم على المُفتاح أنّ يتواجد خلاله إما بالثواني أو باستخدام طابع الوقت الخاصّ يونكس (Unix Time stamp)، ويتحكم الأمر EXPIRE بالمُدّة الّتي يتوجب على المُفتاح التواجد فيها، ويَعرض الأمر TTL الوقت المُتبقي حتّى انتهاء الصلاحيّة. > SET classified:information "Secret Stuff" OK > EXPIRE classified:information 45 (integer) 1 > TTL classified:information (integer) 31 وفي مُحاولة لاستعادة المعلومات بعد انقضاء مدّة الصلاحيّة، ستكون النتيجة هي nil: > GET classified:information (nil) التزايد أو العلاوة (Incrementing) يَملك Redis القدرة على زيادة سلاسل الرموز (strings) في قاعدة بياناته، مع الانتباه أنّه في حال وجود عمليّة جارية في زيادة قيمةٍ ما، فلا يستطيع أمر آخر التدخل والتعديل، هذا الأمر من شأنه أن يجعل من قاعدة البيانات ثابتة وغير شائبة. > SET population 6 OK > INCRBY population 10 (integer) 16 > INCR population (integer) 17 المداولات (Transactions) يملك Redis القدرة على إنجاز المُداولات (transactions)، والّتي يجب أنّ تخضع إلى: تنفيذ الأوامر بالترتيب، ولن يتمّ مقاطعتها خلال العمليّة من قبل طلبات أُخرى. على المُداولات أنّ تُعالج كلها دفعةً واحدة. تبدأ المُداولات بالأمر MULTI وبعد ذلك لتنفيذها يتمّ استخدام الأمر EXEC. سيتمّ الخروج من المُداولة، عند أي مُقاطعة للعمليّة، وسيواجه Redis خطًا سيوقفه من إعادة التشغيل حتّى تنفيذ الأمر edis-check-aof ليتمّ التراجع عن المُداولة الجزئية الّتي تمّت بالفعل قبل حدوث الخطأ وحذفها. سيَتمكّن الخادم بعد ذلك من إعادة التشغيل. > MULTI OK > SET population 6 QUEUED > INCRBY population 10 QUEUED > INCR population QUEUED > EXEC 1) OK 2) (integer) 16 3) (integer) 17 أنواع البيانات في Redis يَملك Redis خمسة أنواع من البيانات: Strings Sets Sorted Sets Lists Hashes سلاسل الرموز (Strings) تُعتبر سلاسل الرموز جوهر وأساس الأنواع في Redis الأوامر التّالية هي الأوامر الشائعة مع سلاسل الرموز: SET: يُعيّن قيمة إلى مُفتاح. GET: يجلب قيمة من مُفتاح. DEL: حذف مُفتاح وقيمته. INCR: زيادة قيمة مُفتاح. INCRBY: زيادة قيمة مُفتاح بقيم مُحدّدة. EXPIRE: الوقت اللازم على المُفتاح التواجد به، ويُعيّن بالثواني. تُستخدم سلاسل الرموز في تخزين الكائنات (objects): > SET newkey "the redis string begins" OK > GET newkey "the redis string begins" المجموعات (Sets) يُمكن الجمع بين سلاسل الرموز (strings)، ولذلك يُمكن استخدام مجموعات Redis، والّتي يُمكن القول عنها أنها مجموعة من سلاسل الرموز غير المُرتبة. بعض الأوامر الشائعة مع المجموعات: SADD: إضافة عنصر أو عناصر إلى مجموعة. SMEMBERS: جلب جميع العناصر المُعيّنة. SINTER: إيجاد تقاطع أكثر من مجموعة. SISMEMBER: التأكّد من وجود قيمة في مجموعة. SRANDMEMBER: جلب عنصر عشوائي. يُستفاد من المجموعات في Redis في العديد من الحالات، ولأن كل عنصر من مجموعة هو فريد، فإن إضافة عنصر إلى المجموعة لا يتطلّب عمليّة “افحص قبل الإضافة”، بدلًا من ذلك، فإن المجموعة ستتأكّد فيما إذا كان العنصر مكرّرًا أم لا، وذلك عند أي تنفيذ للأمر SADD: > SADD colors red (integer) 1 redis 127.0.0.1:6379> SADD colors orange (integer) 1 redis 127.0.0.1:6379> SADD colors yellow (integer) 1 redis 127.0.0.1:6379> SADD colors orange (integer) 0 redis 127.0.0.1:6379> SMEMBERS colors 1) "red" 2) "yellow" 3) "orange" يُستفاد من المجموعات بشكل خاصّ في فحص وضبط عناوين IPs في حال أنها فريدة لزوار صفحة ما، أو مثلًا في استخلاص عناصر بشكل عشوائي بالأمر SRANDMEMBER. المجموعات المرتبة (Sorted Sets) إن المجموعات المُرتبة هي اسمٌ على مُسمّى، فهي مجموعة من سلاسل الرموز (strings) مُرتبطة مع رقم مع ترتيب، وهو بشكلٍ افتراضيّ من الأصغر إلى الأكبر. يَعمل هذا النوع من البيانات بشكل مُلائم جدًا مع المجالات (ranges)، وبسبب التّرتيب الخاصّ به، فإن إضافة وحذف وتحديث القيم تتمّ بسرعة ملحوظة. بعض الأوامر الشائعة مع المجموعات المُرتّبة: ZADD: إضافة عناصر إلى مجموعة مُرتّبة. ZRANGE: عرض عناصر مجموعة مُرتّبة. ZREVRANGE: عرض عناصر مجموعة مُرتّبة بشكل عكسي. ZREM: إزالة عناصر من مجموعة مُرتّبة. يُمكن إنشاء مجموعة مُرتّبة كما في المثال التّالي، وهو لمساحة بعض البلدان، فعلى الرغم من تعيينها بشكل عشوائي، فإن استعراضها يَكون على التّرتيب: > ZADD countries 9 Tuvalu (integer) 1 > ZADD countries 62 Liechtenstein (integer) 1 > ZADD countries .7 Monaco (integer) 1 > ZADD countries .2 VaticanCity (integer) 1 > ZADD countries 107 Seychelles (integer) 1 redis 127.0.0.1:6379> ZRANGE countries 0 -1 1) "VaticanCity" 2) "Monaco" 3) "Tuvalu" 4) "Liechtenstein" 5) "Seychelles" القوائم (Lists) تُعتبر القوائم في Redis مجموعة من القيم المُرتّبة، وبالتّالي فهي عكس المجموعات (Sets)، والّتي هي غير مُرتّبة، فيُمكن إضافة عناصر إلى بداية ونهاية قائمة، حتّى بوجود ملايين العناصر داخل القائمة، وبسرعة كبيرة جدًا. الأوامر الأكثر شيوعًا مع القوائم: LPUSH: إضافة قيمة في بداية قائمة. RPUSH: إضافة قيمة في نهاية قائمة. LPOP: جلب وإزالة العنصر الأوّل في قائمة. LREM: إزالة العناصر من قائمة. LRANGE: جلب مجال من العناصر من قائمة. LTRIM: تعديل قائمة بترك عناصر مُحدّدة من مجال. أمثلة: > RPUSH lunch.provider alice (integer) 1 > RPUSH lunch.provider bob (integer) 2 > RPUSH lunch.provider carol (integer) 3 > RPUSH lunch.provider don (integer) 4 > RPUSH lunch.provider emily (integer) 5 عند الرغبة بالدفع (push) إلى واجهة صف الانتظار (queue)، فمن المُمكن استخدام الأمر LPUSH: LPUSH lunch.provider zoe (integer) 6 يُمكن استخدام الأمر LRANGE لاستعراض عناصر القائمة جميعًا: LRANGE lunch.provider 0 -1 1) "zoe" 2) "alice" 3) "bob" 4) "carol" 5) "don" 6) "emily" تُستخدم القوائم في مُعظم الأحيان لإنشاء خط زمني للأحداث، أو في الحفاظ على مجموعة مُحدودة من العناصر المبعثرات (Hashes) تُعتبر المبَعثرات في Redis من الأدوات المُفيدة في تمثيل الكائنات (objects) ذو العديد من الحقول (fields)، فهي مُعدّة لتخزين عدد هائل من الحقول في حجم صغير، وتستطيع البَعثرة تخزين أكثر من أربعة مليارات من أزواج قيم الحقول (field-value). بعض أوامر البعثرة الأكثر شيوعًا في Redis: HMSET: تعيين قيم بعثرة مُتعدّدة. HSET: تعيين حقل البعثرة بقيمة سلسلة رموز (string). HGET: جلب قيمة حقل البعثرة. HMGET: جلب جميع القيم من حقول البعثرة المعطاة. HGETALL: جلب جميع القيم لبعثرة ما. تُستخدم البَعثرة في وصف حساب المُستخدِم: > HMSET user:1 username someuser password 4bAc0s email someuser@example.com OK > HGETALL user:1 1) "username" 2) "jsmith" 3) "password" 4) "4bAc0s" 5) "email" 6) "jsmith@gmail.com" يتمّ استخدام الأمر HMGET للبحث عن معلومة مُحدّدة، وعرض القيم للحقول المطلوبة فقط. > HMGET user:1 username email 1) "someuser" someuser@example.com الختام انتشر Redis بسرعة كبيرة، واكتسب شهرةً واسعة بين المواقع أمثال: Github, Flickr, Disqus، وغيرها من المواقع، وذلك لتوافقه مع مُعظم لغات البرمجة، فهو بالفعل تقنية مُفيدة يُنصح بالاعتماد عليها وتجربتها. ترجمة -وبتصرّف- للمقال How To Install and Use Redis لصاحبته Etel Sverdlov.1 نقطة
-
في خضم العمل مع العُملاء السيناريو التّالي قد يحدث كثيرًا: - المُصمّم: قد انتهيت من العمل على الجزء الأخير للمشروع الخاصّ بك، وهذا هو تصميم الجزء الخاصّ بنموذج “اتصل بنا”. ما رأيك؟ - العميل: لا يعجبني. يهاب مُعظم المُصمّمين "لم يُعجبني"، ويُعتبر من أكثر ردود الفعل المُحبطة الّتي قد تصدر من العميل أو مدير المشروع أو حتّى زميل العمل. الفكرة هنا والمُراد تسليط الضوء عليها غير مُرتبطة في نفسية المُصمّم أو ما قد يؤثر عليها فقط مع هذا النوع من الردود، إنما الأمر يتعلّق بالنقد البناء الذي يقدم قيمة حقيقية للتصميم والمشروع ككل. يتمّ الحصول على نتائج أفضل عندما تكون الاستجابة وردود الفعل (feedback) قائمة على فهم حاجات ومُتطلبات المُستخدِم، ولذلك يجب التأكد أنّ هذا النوع من الردود غير نابعٍ عن رأي العميل وذوقه الخاصّ، والذي قد يكون رأيًا صائبًا، ولكن ليس بالضرورة أن يكون الرأي الأنسب للمُنتج النهائيّ. إن مسألة الجماليّة هي مسألة مُرتبطة بالذوق، والتّصميم ليس فقط مجموعة من الجماليات، فبالطبع هناك قرارات تُتّخذ في مسألة الجماليّة ولكنها ليست الأمر الفصل، وهي تُتّخذ على مُستوى التصميم ككل. إنّ هذا التصميم بشكله النهائي سيكون موجّهًا إلى الجمهور المُستهدف له، ولذلك ومن ناحية موضوعيّة فإن مسألة الجماليّة هي مسألة يجب أخذها بعين الاعتبار من دون شك. قد يُبدي معظم العُملاء آراءهم بناءً على ذوقهم الخاصّ، محاولين بذلك تبصّر ذوق الجمهور المُستهدف، أو قد يحاولون حل المُشكلة المعنيّة للمُستخدِم. لا يُفترض في هذه الحالة أخذ الرد “لا تعجبني” بمعناه السطحي، والتسليم به على أنّه أمرٌ لا مهرب منه. كيف يتمّ الحصول على استجابة/رد أفضل؟يتمّ الحصول على استجابة إيجابية أفضل ونقدًا بناءً من العميل عبر حثّه على تفسير الأسباب خلف الرد أو الاستجابة السلبية، والّتي عادةً ما تكون هذه الردود تستخدم صيغة من نوع “لا يعجبني بسبب…” هذه الصيغة من الردود تُقدّم استجابة ذو قيمة للمُصمّم وللمشروع ككل. - المُصمّم: لماذا لم يعجبك تصميم نموذج “اتصل بنا” الجديد؟ - العميل: لم يعجبني بسبب أنّ الخط المُستخدَم كبيرٌ جدًا. قد يكون تحقيق أهداف الجمهور مع المُنتج هو العامل الرئيسيّ في نجاحه، ولذلك يجب على العُملاء أنّ يعوا بأنّهم قد لا يستطيعون تمثيل الجمهور المُستهدف. لن يكون ذلك بالأمر الهَيّن لكي يتمّ إدراكه خصوصًا للشخص القريب من المُنتج (العميل). على الطرف الآخر قد يكون المُصمّم نفسه واحدًا من المُستخدمين لهذا المُنتج الّذي يقوم بتصميمه، ولكن يجب الأخذ بعين الاعتبار أنّ المُنتج هنا لا يتمّ تصميمه فقط من أجل مُستخدمين من نوع واحد دون الآخر، حيثُ أنّ المُنتج له جمهور مُعيّن، مع هدف مُحدّد. عندما يعي المُصمّمون أهميّة المُستخدِم النهائي، سيتمكنون عندها من إعادة بناء ردود فعلٍ/استجابة (feedback) أفضل عبر التّساؤل بشكل متجرد على النّحو التّالي: “كيف سيتقبّل المُستخدم النهائيّ هذا التصميم؟” - المُصمّم: هل تظن أنّ المُستخدم النهائيّ سيجد أن الخط المُستخدَم كبير الحجم؟ - العميل: نعم. وأظن أنّه يفضل رؤية كامل مُحتوى الصفحة من دون الحاجة إلى تحريكها (scrolling) صعودًا ونزولًا لرؤية كامل المُحتوى. - المُصمّم: سيكون الخط صغيرًا جدًا في حال قمنا بتصغيره ليتناسب مع الصفحة، الأمر الّذي قد يجعل من النموذج صعب القراءة. - العميل: لا بأس. فجميع مُستخدمينا من فئة الشباب، ويملكون بصرًا جيّدًا. يجب على المُصممين مراجعة الآراء والافتراضات الخاصّة بهم حول المُستخدِم النهائي. كما يجب عليهم التأكد أنّ أي ردٍ أو استجابة يحصلون عليها ليست مقرونةً أو قائمةً على أساسٍ أو افتراضٍ لا صحة له. بمعنى إن قال العميل: أنّ المُستخدِم النهائيّ لن يعجبه التصميم، يجب السؤال هنا عن السبب، أي يجب الكشف عن خلفيّة هذا الافتراض، وما الذي دعا إلى افتراضه في الأساس. - المُصمّم: هل يُمكننا التأكد من أنّ جميع العُملاء هم من فئة الشباب؟ وهل يُمكن القول أنّ جميع الشباب تملك حدّة بصر جيّدة؟ فربما نحن بهذا نخاطر بخسارة عُملاء محتملين عندما لا يكون الموقع سهل القراءة للجميع. يبرز سؤالٌ مهمٌ هنا وهو كيف يُمكن للمُصمّم أنّ يفصل بين الافتراض (الّذي قد يكون غير موضوعيًّا) وبين المعرفة الحقيقية (والّتي تكون صحيحة بطبيعة الحال). إن أي نظرة عامّة حول سلوك المُستخدم خاصّة تلك الّتي تفترض أنّ جميع المُستخدِمين يتشاركون في صفات وميزات شائعة سيكون من المُفترض التحقق منها والتشكيك فيها. يُمكن الحصول على هذا النوع من الافتراضات المُسبقة عبر الاطّلاع على بعض البحوث واستطلاعات الرأي حول المُستخدم، وبذلك سيملك المُصمّم دليلًا واضحًا يُمكن الرجوع إليه عند الضرورة. السر في المناقشةلا يجب أن يعتمد المُصمّم على وصف الآخرين لمشكلتهم وألا يتوقع منهم معرفة استخدام الكلمات الصحيحة والمعبرة عند وصفهم للسّبب الذي يدفعهم إلى الاعتقاد بوجود خلل ما في التصميم. لذلك على المُصمّم أن يَعرف كيف يَطرح السؤال الّذي من شأنه أن يحثّ العميل أن يُقدّم نقدًا بناءً واستجابة ذات قيمة تعود على المشروع والمُنتج بالفائدة المرجوّة. كان قد تمّ الكتابة عن كيف يُمكن اجتناب المشاكل قبل حدوثها (المُستقلّ الحذق هو من يصنع العميل الجيّد) عن طريق شرح وتفسير التصميم عند مشاركته مع الآخرين، ولكن من الصعب شرح كل تفصيل من تفصيلات العمل والعلاقة بينهما. إنّ كان العميل لا يستطيع أن يعبّر عما يجول في خاطره ولماذا هو لا يحب التصميم النهائي ككل، فربما تقديم التصميم بعناصره وجزئياته الصغيرة قد يزيد من فرصة ضبط وتحديد الجزء الذي يُسبب المُشكل لدى العميل. - المُصمّم: أي جزء من الخط يبدو بأنه كبير الحجم بالنسبة لك؟ - العميل: عناوين حقول النموذج (form labels). عندما يقوم المُصمّم بتحديد المكوّن أو الجزء الّذي يُسبب مُشكلة للعميل، عندها عليه أنّ يستخلص بعض الأسباب المُحتملة والّتي قد لا تكون ذو صلة مع المُشكلة. - المُصمّم: هل تعتقد أنّ حجم عنوان الحقل لا يترك مساحة كافية لبقية العناصر، والذي يجبر المُستخدم على التدرّج بين أعلى وأسفل الصفحة؟ - العميل: نعم. من المُفترض جعل عنوان الحقل يبدو بحجم أصغر مما هو عليه الآن. عندما يكون المُصمّم هو المُتحكمتُعتبر الجماليّة مسألة ذوق تختلف من شخصٍ لآخر، إن أي شخص يعرف ما يحب وما يكره من الألوان، والّتي تحدد ما يختاره من لباس إلى أمورٍ أُخرى، وفي نفس الوقت على هذا الشخص أنّ لا يتوقع من الجميع مُشاركته تفضيله لنفس الألوان الّتي يحبها. كان قد كتب “Nishant” سطورًا من ذهب بعنوان “الذوق الجيّد لا يقدِّم ولا يؤخِّر” لخصها بشكل مُتقن عندما قال: عندما يُصبح الذوق أمرًا عظيمًا- المُصمّم: ولكن إن تمّ تصغير الخط المُستخدَم، سيجعل ذلك من القراءة أمرًا عسيرًا، كما أنّ معظم الصفحات تتطلّب تصفّحها صعودًا ونزولًا، وبالتّالي فإن ذلك لن يُسبّب أيّة مشاكل للمُستخدِم. هل تعتقد أنّ النموذج طويل بعض الشيء، وهو ما قد يُنفّر المُستخدم من ملئه كاملًا؟؟ - العميل: نعم، أرغب أنّ يكون نموذج “اتصل بنا” أبسط من ذلك. - المُصمّم: ما رأيك أنّ نتخلّى عن جميع الحقول الحاليّة ماعدا الحقل الخاصّ بـ “البريد الإلكتروني” وحقل “الرسالة”، حيثُ أنّ ذلك كل ما هو مطلوب في حقيقية الأمر؟ - العميل: نعم، هذا يجعل من النموذج أبسط وأكثر وضوحًا. عندما يقوم المُصمّم بتقديم وعرض بعض الاقتراحات، عليه أنّ لا يدع العميل يقول: “نعم” للاقتراح الأول، حيثُ أنّ هذه الاقتراحات لا يقصد بها بأن تكون مخرجًا سريعًا من النقاش، أو أنها طرحت فقط لتغيير شيءٍ ما ليناسب ذوقه. بل هي فرصة للدخول في عصف ذهني (brainstorm) هدفه تحريض الأفكار وخلق بدائل وحلول مُحتملة بشكل مُباشر وسريع، وهو أمرٌ هام يستدعي العمل بشكل تعاونيّ بين المُصمّم والعميل، ولذلك على المُصمّم عدم التغريد خارج السرب واختيار البدائل الأولى بنفسه. إن استطاع المُصمّم استنباط طريقة مُناسبة للعمل بها بينه وبين العميل، حينها سيكون الحلّ المُقترح حلًا ذو قيمة يعود بالنفع على الطرفين، حيثُ أنّ العميل سيكون أكثر التزامًا مع هذا التصميم بما أنّه كان طرفًا في اتخاذ القرار، خاصّة عندما يقطف ثمار هذا التعاون المُشترك، ويُدرك أن هذه النتيجة هي الأمثل الّتي تكللت بتعاون كلا الطرفين. ترجمة وبتصرّف للمقال I Don’t Like It لصاحبته Laura Kalbag.1 نقطة
-
لايوجد فرق فعلي جميع المقارانات بين لغات البرمجة خاطئة المبرمج المتقن لphp يستطيع فعل إي شيء يصنع بruby والعكس صحيح اذًا اللغة التي تجد نفسك تريد تعلمها , إبدأ بها ولاتنظر إلى المقارانات لكن من الناحية التجارية php لها سوق كبير جدًا على عكس ruby هذا لايمنع ان يربح مُبرمج ruby الماهر أكثر.1 نقطة
-
ليست جودة عملك هي ما يحدّد مدى نجاح مشروعك؛ بل ما يحدّد مدى نجاحهِ جودةُ تواصلك مع العميل. بجدّ! فكر بالأمر كما يلي: لقد حققت أفضل إنجاز عملت عليه طوال حياتك بتصميم شعار (logo)، أو ببرمجة تطبيق، أو بكتابة مقال أو إعلان. هذا العمل يجمع كلّ مهاراتك وخبراتك ومعرفتك وعلمك وشغفك. ولكن فيما بعد، تعرض عملك على زبون ولا يعجبه مطلقًا. ليس هذا وحسب، بل يقولون لك أنّ هذا ليس ما أراده أصلًا، وأنّ عليك إعادة بدئه من الصفر، وأنّه يرغب بالاتصال بك هاتفيًّا في الساعة السابعة من صباح اليوم لمناقشته (لم يتبق حتّى ذلك الوقت أكثر من بضع ساعات). لن أقول بأنّي حذرتك، وذلك لأننا جميعًا عرضة لنتائج كهذه عندما لا نتواصل بشكل فعّال. بغض النظر عن مدى جودة عملك، فعليك فهم احتياجات العميل، والتعبير عن سبب كون العمل الذي تعرضه عليهم هو المطلوب لتلبية احتياجاتهم. عدا ذلك، فلا قيمة لعملك بأكمله. إذا كان التواصل الجيد أساس المشروع الناجح، فماذا بإمكانك أن تفعل لضمان حدوث ذلك؟ تحديد ما يُفترض تسليمه بدقّةإذا لم يكن كلا الطرفين واضحين فيما يتعلق بما يفترض تسليمه، والعملية المطلوبة، والجدول الزمني، ومن المسؤول عن كل جزء – كتابيًّا، وقبل الحديث عن المال – فأنت تقفز في مياه لا تعلم مدى عمقها. عليك أيضًا أن تعلم مدى معرفة العميل بما سيكون مسؤولًا عنه. على سبيل المثال: إذا كنت تبني موقعًا له، فهل يعلم كيف يستخدم نظام إدارة محتوى (CMS)؟ إذا لم يكن العميل يعرف ما تفعله، فعليك أن تفهّمه ذلك بطريقة بسيطة (عدا ذلك، لن يقدّر قيمة ما تقدّمه له). وضّح خبراتكلا يكفي أن تخبر العميل بأنك تستطيع إنجاز العمل. عليك أن توضّح لمَ أنت الشخص المناسب للقيام بهذا العمل، وكيف ستحل المشكلات التي يواجهها، وكيف ستوصله إلى أهدافه. قم بهذا في وقت مبكّر قدر الإمكان، بحيث يبدأ بأخذك بعين الاعتبار كخبير وليس فقط كعامِل مُستأجَر. حدّد أهدافك ونجاحاتك بدقّةما أهداف المشروع؟ وكيف سيتم قياس مدى النجاح؟ بغض النظر عن ما تفعله، سيغذّي هذا التواصلُ أيّةَ مناقشات أثناء العمل على المشروع. يريد العملاء نتائج قبل أيّ شيء آخَر؛ حدّد بدقة ما يعني هذا. التواصل الأسبوعيّبالنسبة للمشاريع طويلة الأمد، تواصل مع عميلك مرة واحدة أسبوعيًّا على الأقلّ. بيّن ما وصلت إليه، وما أتممته، وما المميز، وما المطلوب أن يفعله العميل الآن أو الأسبوع القادِم. هذا سيبقي الأمور في مسارها، وإذا خرج شيء ما عن مساره، فمن الأفضل تصحيح المسار في أسرع وقت. التزم بالمطلوبإذا كانت مهمة ما غير مدرجة في "المتطلبات" (deliverables)، فلا يُفترض بك أن تُنفّذه مجانًا. كن لطيفًا ولكن حازمًا عندما يطلب عميل شيئًا ما خارج نطاق المطلوب. أحيانًا يكون خطأً من العميل بحيث لا يكون عارفًا بخفايا الموضوع، وأحيانًا يكون محاولةً للحصول على أكثر مما يدفع مقابله. تأكّد من وجود خطّة لما بعد المشروع أيضًا؛ إذا كان العميل راغبًا في أن تقوم بالمزيد من العمل بعد انتهاء المشروع، فكيف سيتم الأمر؟ حدّد خط انتهاء لكل مشروع، ليس فقط من حيث وجود تاريخ ونهاية محدّدة للمشروع، بل وحتى وجود فرصة للعمل مستقبلًا (بالمشروع، أو بالتعاقد، إلخ). ضع حدودًاإذا كان المشروع يتطلب الكثير من التواصل الحيّ، فأعلِم العميل متى تكون بالعادة متاحًا، ومتى لا تكون مشغولًا بالعمل. فمثلًا، يمكنك تحديد ساعات عملك من الساعة العاشرة صباحًا وحتى الخامسة مساءً بتوقيت جرينيتش، من يوم الإثنين وحتى الخميس. إذا راسلك أو تواصل معك خارج هذا الوقت، فستكون قد أخبرته مسبقًا بأنك غير متاح. اعلم أنك إذا استجبت للبريد أو المكالمات خارج هذه الأوقات، فأنت تظهر له أنك لا تحترم الحدود التي وضعتها، وبهذا لا يلتزم هو أيضًا. أعد صياغة طلباته وأفكاره بلغتك الخاصّةعندما يسأل العميل عن شيء ضمن حدود العمل، فأكّد بالضبط ما يريد عمله بلغتك الخاصّة. هذا يعطيه فرصة أخرى ليتأكد مما إذا كان هذا ما يريده حقًّا. هذا يجنبك سوء الفهم، ويجنبك كذلك الطلبات الهشّة (حيث يمكن أن يغيّر رأيه). تحدّث عن خبراتكليس عليك أن تكون هجوميًّا، ولكن إذا طلب العميل شيئًا تعلم أنّه غير صحيح أو أنه لن يساعده على تحقيق أهدافه، فأعلمه بذلك. يدفع العميل لك لأنك الخبير، ولأن رأي الخبير له وزنه. استمع بشكل جيّدمن المذهل مقدار ما يمكنك تعلمه عن طريقة تقديم عمل رائع لعميلك عندما تستمع إلى ما يريد قوله. وأكثر من هذا، استمع لما وراء ما يقوله [أو كما نقول، اقرأ بين السطور]، لأنّه يمكن أن يفتقر العميل للبلاغة الكافية للتعبير عنه. فمثلًا، إذا كان عميل ما يريد 100 صفحة من المسجلين لمجلته، فما يريده بالفعل هو المزيد من المسجّلين، وهو ما سيزيد من مبيعات منتجه. اسأل "لماذا؟" للوصول إلى احتياجاته الحقيقيّة. وحتى مع وجود تواصل مثاليّ، فلا يمكنك أن تضمن استجابة مثاليّة من كلّ العملاء. يمكن أن لا يقوموا بالأمر ضمن حدود الجدول الزمنيّ، أو حتى أسوأ من هذا، كأن يتوقفوا عن التواصل نهائيًّا. في هذه الحالات، عليك أن تتذكّر أنه حتى إن لم يكن العميل يتصرف بمهنيّة ويلتزم بما عليه فعله في المشروع، يتحتم عليك أن تكون متحضّرًا وخدومًا. شخصيًّا وجدت أنه في غالب الأوقات إذا توقف العميل عن الاستجابة، أو إذا لم يلتزم بالمواعيد، فهذا بسبب أمرين: 1) هناك شيء شخصيّ يحدث في حياته (ولا يمكنك عمل شيء حيال الأمر)، أو 2) أو أنّه مرتبك أو مُرهَق أو مشغول جدًّا، أو لا يعرف كيف يفعل المطلوب منه. في هذه الحالة، كل ما يمكنك فعله هو أن تعرض عليه المساعدة – ليس بالقيام بشيء خارج نطاق العمل، بل بتوضيح شيء ما مجدّدًا أو بأن تعرض عليه أن يسلّم العمل الذي يقوم به إلى شخص آخر. فمثلًا، معظم عملاء مشاريع تصميم المواقع يتكاسلون عن إضافة وتعديل محتوى مواقعهم. إذا حدث هذا معي، فأول ما أفعله هو أن أقترح عليهم كاتب محتوى أو محرّرًا أو مساعدًا عن بُعد ذي خبرة ليساعدهم في وضع المحتوى في الموقع. إنّ أولئك الذي لا يُرهَقون أثناء كلّ مشروع وذلك لأنّ عملاءهم يطلبون أشياء "خاطئة" أو "غبيّة" يعلمون أن التواصل الجيد من البداية وحتى النهاية هو ما يجعل المشروع ناجحاً وممتعاً بالفعل معًا. العمل الذي تقوم به لعميلك مهمّ، ولكن الطريقة التي تتواصل بها معه عن العمل لها نفس الأهميّة. ترجمة – وبتصرف – للمقال How Communication Determines A Project’s Success.1 نقطة
-
يُمكن استخدام أمر find لهذا الغرض. على افتراض أنّك موجود في الملفّ حيثُ توجد الملفّات، نفّذ الأمر التّالي: find . -type f ! -iname "*.mp3" -deleteتُشير النّقطة (.) إلى أنّ البحث يجري في المجلّد الحالي. خيّار type f- يحدّد نوعيّة الملفّات الّتي نُريد حذفها (ملفّات عاديّة وليست ومجلّدات). علامة ! تُشير إلى نفي المعيار الموجود بعدها، وهو أنّ اسم الملفّ ينتهي بmp3. وdelete- تحدّد الإجراء المطبّق على نتيجة البحث. يُقرأ الأمر "ابحث عن الملفّات الموجودة في المجلّد الحالي والمجلّدات المتفرّعة منه الّتي لا تنتهي أسماؤها بmp3 واحذفها".1 نقطة