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

محمد أحمد العيل

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

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

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

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

    27

كل منشورات العضو محمد أحمد العيل

  1. يتناول هذا المقال طريقة منهجية ستساعدنا في تطوير آليات للتعرّف على المشاكل التي تواجهها شبكات المبدّلات، وبالتالي تحديد مشاكل الاتصال المحتملة على المنافذ، ممّا يخوّلنا التعرف على المشاكل الحاصلة في شبكات VLAN وعند التوصيل بالجذع (Trunking) أو حتى كشف اختلالات العمل في بروتوكول توصيل شبكة VLAN بالجذع (VTP) أو في بروتوكول الشبكة الممتدة (STP). تشخيص حالة المبدّلات لا يعدّ فحص شبكات المبدّلات وتشخيصها علمًا دقيقًا، بل هو أقرب للفن. يكون لدينا عادةً حدس حول جزء الشبكة الذي توجد به المشكلة أو يتسبّب فيها. في ما يلي اقتراحات عامة لجعل تشخيص مشاكل شبكات المبدّلات أكثر فاعلية. أوّلًا، يجب أن تعلم الإعدادات المضبوطة على شبكة المبدّلات محل التشخيص. يجب في هذا الإطار معرفة الخدمات المُفعَّلة على المبدّلات. ثانيًّا، يجب أن تكون لدينا معرفة دقيقة بالمُخطَّط الفيزيائي والمنطقي للشبكة، لكي تكون لدينا رؤية واضحة عن كيفية ترابط الأجهزة، فيزيائيًّا ومنطقيًّا، ممّا يساعدنا في تتبع المسارات. وأخيرًا، يُفضَّل عند تشخيص المشاكل والبحث عنها أن تكون لدينا خطة نسير عليها بدلًا من القفز هنا وهناك. المنهجية التي أتبعها شخصيًّا هي سياسة نموذج OSI ذي الطبقات السبع. أبدأ عادة بالمستوى الأول وأتأكد من أنّ الطبقة الفيزيائية تعمل على نحو صحيح قبل أن أنتقل إلى الطبقة الثانية للتأكد من أنّ طبقة الربط تعمل كما يُرام. بعد التأكّد من الرابط المحلي أنتقل إلى الخطوة الموالية وهي التأكد من أن الرابط يتخاطب مع الروابط الأخرى التي لا تتصل به مباشرة. في الأخير، لا نفترض أي شيء. لا نفترض أن أيًّا من المكوّنات الأساسية يعمل بدون مشكلة إلّا إذا اختبرناه أولا، فقد يكون شخص آخر غيّر الإعدادات دون أن ينبّه على ذلك أو يوثّق التغيير. إضافة إلى ذلك، لا تتعامل مع التغذية الراجعة من المستخدمين على أنها حقيقة ثابتة، لأن المستخدمين يعطون أحيانًا معلومات مضلّلة. قبل سنوات كنتُ أفحص الشبكة وأخبرني مستخدم أنّ الشبكة لا تعمل، لذا افترضتُ بداية أنّ الرابط لا يعمل. تبيّن لي بعد ذلك أنّ ما يعنيه هو أنّ الشبكة بطيئة جدّا. غني عن القول أنّ التحقّق اللازم لمعرفة مصدر المشكل يختلف في شبكة بطيئة عنه في عدم القدرة على الاتصال بالشبكة. تشخيص مشاكل الاتصال عبر المنفذ نوعية الكابل المستخدم واحد من الأمور التي على الفني أو مدير الشبكة فحصها أثناء التحقّق من الاتصال عبر منفذ. إلّا أنّ ذلك المشكل لم يعد مطروحًا في الغالب، لأنّ غالبية الكوابل المستخدمة في المؤسسات الآن هي من النوع Cat 5e. في الماضي، أثناء الانتقال من استخدام كابلات Cat 3 إلى كابلات Cat 5، كان شائعًا تواجد نوعين أو أكثر من الكابلات في الشبكة. لذا، إنْ أوصلت دون انتباه كابلا من النوع Cat 3 بمنفد بسرعة 100 ميغابت للثانية فإن الكابل لم يكن بمقدوره دعم تلك السرعة. الأمر مختلف هذه الأيام. يجب الانتباه كذلك إلى ألّا يُستخدَم كابل ذو طول يتجاوز المئة (100) متر، رغم أنّ ذلك نادر هذه الأيام نظرًا لكون المؤسسات تشتري كابلات مصنَّعة جاهزة، خلافًا لسنوات مضت كان فنيو الشركة هم من يجهّز الكابلات. التحقّق من انتماء منفذ من منافذ المبدّل إلى شبكة VLAN طريقة أخرى لتشخيص مشاكل الاتصال عبر المنفذ. إنْ أسنِد المنفذ لشبكة VLAN غير تلك التي يرتبط بها فلن يمكنه التواصل معها. يجب كذلك التحقّق من أنّ المنفذ لم تعطِّله إحدى الخدمات الأمنية، مثل أمن المنفذ (Port security)، أو مدير نظام آخر عن طريق الخطأ. لذا، حتى وإنْ كان منفذ شبكة VLAN الصحيح فقد يكون عُطِّل خطأً أو عن قصد لأسباب أمنية. لذا يجب الانتقال إلى إعدادات المنفذ وتنفيذ الأمر show interface للتأكد من أن المنفذ نشط ويعمل. في الأخير يجب التأكد من توافق أنماط الاتصال ثنائي الاتجاه (Duplex). عند وقوع مشكلة عدم توافق في نمط الاتصال ثنائي الاتجاه (المزدوج) فإن ما يحدث هو، ليس فقدان الاتصال، بل بطأه، لأن أحد الجانبين يفترض وجود اتصال ثنائي الاتجاه في آن (Full-Duplex)، أي أنه يُرسل بيانات ويتلقى أخرى في الوقت نفسه، في حين يفترض الطرف الآخر أن الاتصال ثنائي الاتجاه بالتناوب (Half-duplex). يعتمد الاتصال ثنائي الاتجاه بالتناوب مبدأ CSMA/CD (الناقل متعدد الوصول مع اكتشاف الاصطدام، Carrier-sense multiple access with collision detection). عندما يريد أحد طرفيْ الاتصال إرسال بيانات فإنه يتحقّق أولًا من خلو الرابط من البيانات القادمة من الطرف الآخر، وفور اكتشافه لها يتراجع عن الإرسال. لذا فإن كل ما يرسله المنفذ ذو النمط Full-duplex لا يتلقاه المنفذ الآخر ذو نمط الاتصال Half-duplex. هذا الأخير لن يرسل أي بيانات إلى المنفذ الآخر لأنه كل ما أراد إرسالها وجد بيانات قادمة من الآخر فيتوقّع أن اصطدامًا سيقع بين البيانات ويتوقّف عن الإرسال. الاستثناء الوحيد هو في حالات السكون، عندما لا يكون لدى أحد المنفذين ما يرسله، وبالتالي يمكن للآخر إرسال بياناته ويتلقاها الآخر. تشخيص مشاكل شبكات VLAN والروابط الجذعية انعدام التوافق هو أحد المشاكل التي قد نواجهها في شبكات VLAN. شبكة VLAN الأصلية بالنسبة لمعيار 802.1q هي إطار بيانات بدون وسم. تتوافق المبدّلات مبدئيًّا على شبكة VLAN الأصلية ذاتها. لذا يجب أن يُسنَد هذا التفضيل إلى شبكة VLAN مشتركة بين المبدّلات جميعا. قد ينتهي المطاف، في حالة انعدام التوافق، بإعادة توجيه إطارات بيانات من شبكة VLAN (رقم 1 مثلًا) على مبدّل، توجيهها إلى شبكة VLAN أخرى (رقم 2 مثلًا) على مبدّل آخر، وبالتالي يحدث سوء فهم. يوجد سبب آخر لمشاكل التوصيل بالجذع وهو انعدام التوافق بين أوضاع الرابط الجذع. تستخدم مبدّلات Cisco بروتوكول التوصيل الديناميكي بالجذع DTP ‏(Dynamic trunking protocol) للتفاوض حول اختيار الرابط الجذع، إلا أنّ هذا البروتوكول قد يواجه مشاكل في التفاوض حول خدمات الرابط الجذع بين مبدّلات Cisco قديمة وأخرى أحدث منها. لذا يُنصَح بإعداد الرابط الجذع يدويًّا كل ما كان ذلك ممكنا. أو ربما يمكن إيجاد تسوية عبر ضبط التوصيل بالجذع يدويًّا على أحد الطرفيْن وترك الطرف الآخر يتفاوض تلقائيًّا حول خصائص الرابط الجذع. يُعاد توجيه إطارات البيانات داخل شبكة VLAN عبر النظر في عنوان MAC الخاص بالوجهة، إلّا أننا نحتاج للمرور عبر موجّه لتسهيل التواصل بين شبكات VLAN. في الوقت نفسه لا يمكن للموجّه معرفة جميع الخواص المتعلّقة بالطبقة الثانية، لذا، ومن أجل تمكين الموجِّه من توجيه البيانات من شبكة VLAN إلى شبكة VLAN أخرى نخصّص لكل شبكة VLAN عنوان IP فريدا، لكي يكون بإمكان الموجِّه توجيه الرزم من عنوان IP إلى آخر، وهو ما يعني بطريقة غير مباشرة التوجيه من شبكة VLAN إلى أخرى. لذا إنْ منحنا شبكتي VLAN الشبكة الفرعية نفسها فستكون لدينا مشكلة. تشخيص بروتوكول VTP ينبغي الانتباه عند تشخيص مشاكل بروتوكول VTP للأمر المستخدَم في عرض الإعدادات. لا يُظهِر الأمر show run معلومات عن إعدادات VTP وشبكات VLAN المتعلقة به، إذا كان المبدّل يعمل وفق وضع الخادوم أو العميل، والسبب في ذلك هو أن معلومات VTP في تلك الحالة تُخزَّن في ملف مستقل يُسمَّى VLAN.dat على مبدّل Cisco. يجب استخدام الأمر show VTP status أو show vlan لعرض الإعدادات المتعلقة ببروتوكول VTP. سنتحدّث في ما يلي عن تشخيص الاختلالات في بروتوكول VTP التي قد تظهر عند إضافة مبدّل إلى الشبكة. ما يحدث عند ربط مبدّل مستعمَل لديه اسم نطاق موافق للنطاق الذي تستخدمه شبكة مبدّلات Cisco التي نربطه بها، ولديه إعدادات تجعله يعمل وفق وضع الخادوم مع رقم مراجعة أكبر من رقم المراجعة الذي تعمل وفقه مبدّلات الشبكة، ما يحدث في هذه الظروف هو أن جميع مبدّلات الشبكة التي تعمل وفق وضع العميل ستبدأ بأخذ معلومات شبكات VLAN من المبدّل الجديد ذي المعلومات المزيَّفة. ستلغي المبدّلات العميلة كل المعلومات القديمة لديها عن شبكات VLAN وتبدأ بأخذ معلوماتها من المبدّل الجديد ذي رقم المراجعة الأكبر. المشكلة في هذه الحالة هي أن خادوم VTP الجديد قد يكون خادومًا مزيَّفًا يمد المبدّلات بمعلومات مزيَّفة. يعني هذا أن جميع المستخدمين الذين كانت لديهم اتصالات بشبكات VLAN قبل الخادوم الجديد سيفقدون تلك الاتصالات، ويتحول لون الإشارات الضوئية على المنافذ في هذه الحالة إلى اللون الأصفر. توجد حالة مماثلة تحدث عند إعادة تشغيل مبدّل، فيفقد المبدّل العميل جميع المعلومات التي استقاها من قاعدة بيانات VLAN، وبالتالي لا يعلم بوجود شبكات VLAN ما عدا الشبكة رقم 1، وبالتالي يصبح المستخدمون الذين لا ينتمون لشبكة VLAN تلك في حالة خمول، مع إشارة ضوئية صفراء، لأن المنافذ ليس لديها شبكة VLAN ترتبط بها. توجد أسباب عدّة لإخفاق بروتوكول VTP في تبادل معلومات VLAN. في ما يلي احتمالات للتحقق منها : تأكد من أنّ المنافذ التي تربط المبدّلات في ما بينها هي جميعها منافذ جذع، لأن بروتوكول VTP لا يعلن عن معلومات الشبكة إلّا عبر الروابط الجذعية. تأكّد من أن المبدّل الخادوم يتوفّر على جميع شبكات VLAN المطلوبة مُعدَّة لنشر معلوماتها. تأكّد من أن المبدّلات لديها خادوم VTP واحد على الأقل لإعلام المبدّلات العميلة بمعلومات VLAN. انتبه عند إعداد اسم نطاق VTP وكلمة السر الخاصة به أنّهما حسّاسان لحالة الأحرف (Case-sensitive). تأكّد من أنّ المبدّلات جميعًا تشغّل الإصدار نفسه من VTP. تحقّق من اسم نطاق VTP وإصداره على المبدّل بالوضع الشفاف. يختلف سلوك المبدّل في الوضع الشفاف حسب إصدار بروتوكول VTP. في الإصدار الأول (VTP 1) يتحقّق المبدّل في هذا الوضع عن اسم النطاق ورقم الإصدار، ولا يعيد توجيه البيانات إلا إذا تطابق هذان الوسيطان مع الإصدار والنطاق اللذين ينتمي لهما المبدّل. أما في الإصدار الثاني فإن المبدّل في الوضع الشفاف يعيد توجيه إعلانات VTP دون النظر في رقم الإصدار أو اسم النطاق. يجب الانتباه إلى أنّ الإصدارين الأول والثاني من بروتوكول VTP لا يدعمان نشر معلومات شبكات VLAN التي يتجاوز معرّفها القيمة 1005 (يُطلَق على شبكات VLAN التي يوجد معرّفها في المجال 1006-4096 اسم شبكات VLAN ذات المجال المُوسَّع Extended range VLAN). تشخيص مشاكل بروتوكول الشبكة الممتدة STP أول ما يجب على مَن يريد تشخيص المشاكل في بروتوكول STP فعله هو أن يكون لديه مُخطَّط شبكة يوضّح المبدّل الذي اختير ليكون الجسر الجذر، وكذلك الروابط التي عطّلها البروتوكول للحصول على شبكة خالية من الحلقات اللامتناهية. الحلقات اللامتناهية هي إحدى المشاكل الأكثر تأثيرًا على أداء الشبكة، وأول ما يدل على حدوثها هو الاستخدام الكبير جدًّا للبث الإذاعي نظرًا لوجود عواصف البث الإذاعي (Broadcast storm). المؤشّر الثاني على الحلقات اللامتناهية هو بطء الشبكة تدريجيًّا ليصل الأمر في النهاية إلى فقدان إمكانية الاتصال نظرًا لوجود الكثير من البيانات التي تدور بين المبدّلات بدون توقّف. المؤشّر الثالث يظهر عند النظر في الإشارات الضوئية على المبدّلات حيث تُظهِر بالتزامن وميضًا شديد السرعة. الحل الأسرع لإيقاف الحلقات اللامتناهية هو إيقاف جميع المنافذ الموجودة على المبدّل المركزي، ثم إعادة توصيلها الواحد بعد الآخر لمعرفة الرابط الذي تسبّب في الحلقة. يمكن كذلك استخدام أوامر التنقيح (Debugging)، مثل الأمر debug spanning tree events، للحصول على تفاصيل أكثر حول ما يحدث على المبدّل الذي يواجه المشكلة. للحؤول دون مشاكل STP فمن الأفضل عدم ترك البروتوكول يختار المبدّل الجذر تلقائيًّا، وبدلًا من ذلك اختيار المبدّل المناسب لذلك يدويّا. وفي الأخير يجب التأكد من أننا نستخدم بروتوكول الشجرة الممتدة السريع للرفع من أداء الشبكة والحصول على حالة مستقرة بسرعة. ترجمة – بتصرّف – للمقال Troubleshooting Switched Network
  2. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يُدير خادوم MySQL الاتّصالات القادمة إلى قاعدة البيانات اعتمادًا على ملفّ مقبس Socket، وهو نوعٌ خاصّ من الملفّات يسهّل التواصل بين عمليّات Processes مختلفة. يحمل الملفّ المقبس الخاصّ بخادوم MySQL اسمَ mysqld.sock ويوجد عادةً - بالنسبة للخواديم العاملة بتوزيعة أوبنتو – في المجلّد ‎/var/run/mysqld/‎. تُنشئ خدمة MySQL هذا الملفّ تلقائيّا. تتسبّب التعديلات على نظام التشغيل أو على إعدادات MySQL في عدم تمكّن MySQL من قراءة الملفّ المقبس، ممّا يمنع الوصول إلى قواعد البيانات على الخادوم. يظهر خطأ المقبس الشائع على النحو التالي: ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2) توجد بضعة أمور تتسبّب في الخطأ أعلاه، وخيّارات محدودة لإصلاحه. أحد الأسباب الشائعة هي توقيف خدمة MySQL أو الإخفاق في تشغيلها؛ بمعنى أنّ الخدمة لم تستطع إنشاء الملفّ المقبس ابتداءً. حاول تشغيل الخدمة بالأداة systemctl لمعرفة ما إذا كان هذا هو سبب ظهور الخطأ: sudo systemctl start mysql ثم حاول الوصول إلى سطر أوامر MySQL من جديد. تأكّد - إن استمرّ الخطأ في الظهور – من المسار الذي يبحث فيه MySQL عن الملفّ المقبس. يمكن معرفة هذا المسار من خلال ملفّ الإعداد mysqld.cnf: sudo nano /etc/mysql/mysql.conf.d/mysql.cnf ابحث عن المُعامل socket ضمن المقطع [mysqld] من الملفّ. يبدو المقطع المذكور كالتالي: . . . [mysqld] user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 . . . أغلق الملفّ ثم تأكّد من وجود الملفّ mysqld.sock بتنفيذ الأمر ls على المسار الذي يتوقّع MySQL وجودَ الملفّ فيه: ls -a /var/run/mysqld/ إذا كان الملفّ المقبس موجودًا فسيظهر في مُخرجات الأمر: . .. mysqld.pid mysqld.sock mysqld.sock.lock إذا لم يكن الملفّ موجودًا فربما يكون السبب هو أنّ MySQL يحاول إنشاءه دون أن تكون لديه الصلاحيّات المناسبة لذلك. يمكن التأكّد صحّة الصلاحيّات بتغيير ملكيّة المجلّد إلى المستخدم والمجموعة mysql: sudo chown mysql:mysql /var/run/mysqld/ ثم تأكّد بعد ذلك من أنّ المستخدم mysql لديه الصلاحيّات المناسبة على المجلّد. تصلُح الصلاحيّات 755 لأغلب الحالات: sudo chmod -R 755 /var/run/mysqld/ أخيرًا؛ أعد تشغيل MySQL لترى ما إذا كان يستطيع إنشاء الملفّ المقبس من جديد: sudo systemctl restart mysql ثم حاول الوصول إلى سطر أوامر MySQL. إن استمرّ خطأ المقبس في الظهور، فقد يشير ذلك إلى وجود مشكلة أعمق في خادوم MySQL، وفي هذه الحالة تجب مراجعة سجلّات الأخطاء بحثًا عن ما يقود إلى معرفة سبب المشكلة. ترجمة – بتصرّف – للمقال How To Troubleshoot Socket Errors in MySQL لصاحبه Mark Drake. اقرأ أيضًا كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL
  3. فلنفترض أنّ لديك مقطع فيديو طويلًا تريد قطع جزء من وسطه دون أن تكلّف نفسك عناء استيراد المقطع إلى محرّر فيديو، وقطع أجزاء منه، ثم تصديره من جديد. هذه الطريقة مملّة، وعلاوةص على ذلك تغيّر ترميز (Transcoding) الفيديو لغير حاجة. توجد طريقة سهلة لقصّ مقاطع الفيديو من سطر الأوامر باستخدام برنامج avconv عن طريق تنفيذ الأمر التالي في سطر الأوامر: avconv -i [input file] -ss [start time] -t [duration] -codec copy [output file] سنعطي مثالًا على كيفيّة تنفيذ الأمر أعلاه. فلنفترض أنّ لدينا مقطع فيديو يبدأ بـ 42 ثانية لا نرغب فيها. نحتاج لتعيين المعامل ‎-ss إلى القيمة 00:00:42. يقبل هذا المعطى رمزًا زمنيًّا بصيغة "ثوان:دقائق:ساعات". يخبر المعامل البرنامج أنّنا نريد الإبقاء على محتوى الفيديو الموجود بعد 42 ثانية. ماذا لو كانت لدينا 12 ثانيّة أخرى غير مرغوب بها في آخر الفيديو؟. في هذه الحالة يمكننا استخدام المعامل ‎-t لتحديد المدّة التي نريد الاحتفاظ بها من الفيديو. يوجد إشكال بسيط هنا، فقيمة المعامل ‎-t هي مدّة زمنيّة وليست نقطة زمنيّة محدّدة، وهذه المدّة هي الطول الإجمالي للمقطع الناتج. يعني هذا أنّ ما كان يوجد عند النقطة الزمنيّة 00:00:42 سيصبح عند 00:00:00 في الملفّ الناتج عن القصّ. وبالتالي إنْ أردنا قصّ 12 ثانيّة من آخر الفيديو فسنحتاج لعمليّة حسابيّة بسيطة. توجد طريقة سهلة تتمثّل في طرح قيمة المعامل ‎-ss من النقطة الزمنيّة التي تريد التوقّف عندها، واستخدام هذه القيمة في المعامل ‎-t. إذا كنّا نريد التوقّف عند النقطة 00:04:08 فإنّ قيمة ‎-t ستكون 00:03:26. نحصُل في النهاية على الأمر التالي: avconv -i input.mkv -ss 00:00:42 -t 00:03:26 -codec copy output.mkv لدينا الآن مقطع فيديو بطول 3 دقائق و26 ثانيّة. هذه هي طريقة قصّ الفيديو باستخدام avconv نظريّا. عمليًّا، قد تحتاج لتغيير بسيط. لسبب مّا لا يُنسَخ الصوت إلى الفيديو الناتج بطريقة جيّدة، لذا قد تحتاج لإعادة ترميز الصوت. إنْ واجهت هذه المشكلة فيمكنك التغلّب عليها بسهولة باستخدام الأمر التالي: avconv -i input.mkv -ss 00:00:42 -t 00:03:26 -c:v copy -c:a flac output.mkv يطلُب الأمرُ من البرنامج بنسخ ترميز الفيديو من المصدر، وفي نفس الأثناء إعادة ترميز الصوت إلى flac، الذي هو ترميز يضغط الصوت بدون فقد للمعلومات. ترجمة – بتصرّف – للمقال How to Trim Videos with the Command Line Using AVConv.
  4. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يحدُث أحيانًا أن تُصاب جداول MySQL بأعطاب سببها أخطاء أدّت إلى استحالة قراءة البيانات الموجودة بها. تؤدّي محاولة قراءة بيانات من جداول معطوبة – عادةً - إلى انهيّار الخادوم. في ما يلي بعضٌ من الأسباب الشائعة لعطب الجداول: توقّف خادوم MySQL أثناء عمليّة كتابة في جدول. التعديل، من طرف برنامج خارجي، على جدول يُعدِّل عليه الخادوم في نفس الوقت. إيقاف الخادوم دون سابق إنذار. إخفاق في عتاد الحاسوب. وجود علّة في برمجة MySQL. إذا كنت تظنّ أنّ أحد جداول قاعدة البيانات معطوب، فيجب أخذ نسخة احتيّاطيّة من مجلّد البيانات قبل تشخيص عطب الجدول أو محاولة إصلاحه.يُساعد هذا الإجراء في التقليل من خطر فقد البيانات. أول ما يجب عليك فعلُه هو إيقاف خدمة MySQL: sudo systemctl stop mysql ثم نقل جميع بيانات MySQL إلى مجلّد جديد. يوجد المجلّد المبدئي لبيانات MySQL بالنسبة للخواديم العاملة بتوزيعة أوبونتو في المسار ‎/var/lib/mysql/‎: cp -r /var/lib/mysql /var/lib/mysql_bkp تصبح جاهزًا – بعد إنشاء نسخة احتيّاطيّة – للبدء في التحقيق لمعرفة ما إذا كان الجدول معطوبًا أم لا. إذا كان الجدول يستخدم محرّك التخزين (MyISAM) فيمكن التحقّق من عطب الجدول باستخدام التعليمة CHECK TABLE من سطر أوامر MySQL: CHECK TABLE table_name; ستظهر رسالة في مُخرجات الأمر تخبرك ما إذا كان الجدول معطوبًا أم لا. إذا كان جدول MyISAM معطوبًا فيمكن عادةً إصلاحه بتنفيذ التعليمة REPAIR TABLE: REPAIR TABLE table_name; ستُظهرمخرجات التعليمة - بافتراض أنّ عمليّة الإصلاح ناجحة – رسالة تشبه التالي: +--------------------------+--------+----------+----------+ | Table | Op | Msg_type | Msg_text | +--------------------------+--------+----------+----------+ | database_name.table_name | repair | status | OK | +--------------------------+--------+----------+----------+ أما إذا لم تنجح العمليّة وبقي الجدول معطوبًا فإنّ توثيق MySQL يقترح بضعة طرق بديلة من أجل إصلاح الجداول المعطوبة. بالنسبة للجداول التي تستخدم محرّك التخزين InnoDB، فعمليّة الإصلاح مختلفة. بدأت قواعد بيانات MySQL باستخدام محرّك InnoDB - الذي يتميّز بتوفّرعمليّات التحقّق التلقائي من الأعطال وإصلاحها - مبدئيًّا منذ الإصدر 5.5. يتحقّق InnoDB من وجود أعطال في الصفحات بحساب مجموع تحقّق (Checksum) لكلّ صفحة يقرأها؛ وفي حال وجود اختلاف بين مجموعات التحقّق فإنّه يوقف خادوم MySQL تلقائيّا. نادرًا مّا توجد حاجة لإصلاح الأعطاب في الجداول التي تستخدم InnoDB، إذ أنّ InnoDB لديه آليّة يمكنها حلّ أغلب الأعطاب عند إعادة تشغيل الخادوم. على الرغم من ذلك، ينصح توثيق MySQL باستخدام طريقة "الطرح وإعادة التحميل" Dump and reload. يعني هذا إعادة الوصول إلى الجدول المعطوب باستخدام الأداة mysqldump، لإنشاء نسخة احتيّاطيّة منطقيّة من الجدول يُحتَفظ فيها ببُنيته وبياناته؛ ثم إعادة تحميل الجدول إلى قاعدة البيانات. حاول إعادة تشغيل خدمة MySQL لترى إن كنت ستتمكّن من الوصول إلى الخادوم، مع استحضار آلية عمل طريقة "الطرح وإعادة التحميل: sudo systemctl restart mysql إنْ استمرّ انهيّار الخادوم أو استحال الوصول إليه لسبب آخر، فربّما يكون من المفيد تفعيل الخيّار force_recovery ضمن عدادات InnoDB. يمكن فعلُ ذلك بسهولة عبر تحرير الملفّ mysqld.cnf: sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf أضف السّطر التالي في المقطع [mysqld] من الملفّ: . . . [mysqld] . . . innodb_force_recovery=1 احفظ الملفّ ثم أغلقه، ثم جرّب إعادة تشغيل MySQL من جديد. إذا استطعت النجاح في الوصول إلى الجدول المعطوب فاستخدم الأداة mysqldump لطرح بيانات الجدول في ملفّ جديد. يمكنك تسميّة الملفّ بما يحلو لك. في ما يلي اخترنا الاسم out.sql: mysqldump database_name table_name > out.sql الخطوة المواليّة هي حذف الجدول من قاعدة البيانات. يمكنك استخدام الصيغة التاليّة لتفادي فتح سطرأوامر MySQL من جديد: mysql -u user -p --execute="DROP TABLE database_name.table_name" أخيرًا، استعد الجدول بالاعتماد على ملفّ الطرح الذي أنشاته للتو: mysql -u user -p < out.sql فليكن في علمك أن محرّك التخزين InnoDB – في العموم – أكثر مقاومةً للأخطاء من محركّ التخزين القديم MyISAM. لا يعني هذا أنّ جداول InnoDB غير معرَّضة للأخطاء، لكن وجود ميزات التغلّب على الأخطاء تلقائيّا يجعل احتمال عطب الجداول وما ينتج عنه من انهيّارات أقلّ بكثير. ترجمة - بتصرّف - للمقال How To Fix Corrupted Tables in MySQL لصاحبه Mark Drake. اقرأ أيضًا المقال التالي: كيف تشخّص أخطاء المقابس Sockets في MySQL
  5. يمكن أنْ يكون تطوير موقع ويب بدون الأدوات المناسبة مدعاة للكثير من الإحباط. ستجد أحيانًا أنّك تعمل على برنامج متقدّم جدًّا بالنسبة لمستواك، أو يمكن أن تكون بحاجة للانتقال إلى برنامج موجّه للمحترفين. يتعلّق الأمر بمستوى قدراتك بوصفك مصمّم مواقع، إلّا أنّ برامج التصميم للويب هي - مجملًا - نفسُها سواءٌ كنت مبتدئًا أو مستخدمًا متقدّمًا. الفرقُ الوحيد هو أنّك قد لا تستخدم جميع الميزات المتوفّرة في أداة معيّنة إنْ كنت مستخدمًا مبتدئًا أو بخبرة متوسّطة. تحتاج لاستكشاف ما يستخدمه المحترفون ومتابعة دورات وقراءة مقالات لمعرفة الطريقة التي تجعل تصميماتك أفضل ما يمكن أن تصل إليه. لذا، سنلخّص في هذا المقال بعضًا من الأدوات وبرامج التصميم التي تحتاجها أكثر من غيرها، ونشرح ما الذي يُستخدَم فيه كلّ واحد منها خلال عمليّة التطوير. ووردبريس ووردبريس هو نظام إدارة محتوى وتدوين بلغت شهرته ومرونته حدًّا جعل ثُلث مواقع الويب الموجودة تستخدمه. تمكن استضافة ووردبريس ذاتيًّا (وبالتالي يكون لديك التحكّم الكامل في موقعك). علاوة على ذلك، فإنّ المنحى التعلّمي لووردبريس سهلٌ مقارنة بمنصّات مثل Weebly وShopify. يتوفّر برنامج ووردبريس مجانًا، لكنْ ستحتاج للدفع مقابل أمور من قبيل اسم النطاق (Domain name)، الاستضافة (Hosting) والقوالب. بالمختصر، تهدف هذه المنصّة الرائعة لبناء مواقع الويب إلى العمل في الخلفيّة وإعطائك فرصة إنشاء صفحات، وتدوينات، وقوالب وغيرها بسهولة. فوتوشوب يشتهر فوتوشوب بتحرير الصوّر والتعديل عليها، إلّا أنّ هذا المحرّر الضخم يصلُح لأي نوع من مشاريع التصميم. يُستخدَم فوتوشوب لإنشاء شعارات لمواقع الويب أو لتصميم نماذج أوليّة لها. ليس غريبًا أن تجد من يستخدم فوتوشوب لإنشاء مخطّط تصميم كامل لموقع ويب، اعتمادًا على نظام الطبقات (Layers) والتصدير الذي يتوفّر عليه فوتوشوب ويوفّر الكثير من الميزات. Sketch يُنظَر إلى برنامج Sketch غالبًا على أساس أنّه إصدار مُبسَّط من فوتوشوب. يعود السبب في ذلك إلى أنّ Sketch يركّز كثيرًا على التصميم للويب، في حين يمكن استخدام فوتوشوب لأي مشروع تصميم. الميزة الأساسيّة لبرنامج Sketch هي آليّة عمله مع التصاميم المتجهيّة (Vectors)، ممّا يجعله رائعًا لتوسعة التصاميم أو تقليصها بدون فقد أي بيانات أثناء العمليّة. علاوةً على ذلك، يمكن اختبار التصميم على الأجهزة الجوّالة مباشرةً من البرنامج. GIMP توجد في GIMP تقريبًا نفس الميزات الموجودة في فوتوشوب. الفرق الأساسي هو أنّ GIMP مجانيّ تماما. يحاجج بعض المصمّمين بالقول إنّ GIMP معقّد الفهم قليلًا، إلّا أنّه منتَج مجانيّ رائع يوفّر ميزات ضروريّة للعمل على تصميم المواقع وتحرير الصوّر. Adobe Dreamweaver يعدّ Dreamweaver مصنعًا لتصميم المواقع، حيث يوجد محرّر شفرة برمجيّة على جانب الشاشة ومعاينة لموقع الويب على الجانب الآخر. يمكنك بهذه الطريقة التعديل على الشفرات البرمجيّة ورؤية النتيجة مباشرة. في حين يركّز فوتوشوب على إنشاء عناصر موقع الويب أو النماذج الأوليّة، فإنّ Dreamweaver ينشئ موقع ويب جاهزًا للعمل. يمكن بنهاية العمليّة تصدير ملفات موقع الويب ووضعها على الخادوم. WAMP الخطوة المواليّة لتصميم موقع باستخدام برنامج مثل ووردبريس أو Dreamweaver هي اختباره، ثم تعديله وفقًا لنتيجة الاختبارات. قد لا ترغب في الدفع مقابل خادوم، فقط لعرض موقع نصف مكتمل على الجمهور. توجد طريقة أفضل، وهي الحصول على خادوم محلّي مثل WAMP. بالمختصر، تشغّل الخادوم على حاسوبك الشخصيّ، وتنشئ أو تختبر موقع الويب كما لو كان موجودًا على الشبكة، وأخيرًا تنقل الملفات إلى الخادوم المتاح للعموم. يعمل برنامج WAMP على أنظمة التشغيل وندوز، ولكن يوجد بديل يعمل على حواسيب Mac وهو MAMP. عجلة الألوان (Color Wheel) من المهمّ جدًّا أن تكون لديك أداة ألوان أثناء تصميم موقع ويب. عجلة الألوان أداة مجانيّة توفّر طريقة لبناء مخطّطات ألوان ذات مظهر متناسق لاستخدامها ضمن موقع الويب. قد تكون لديك فكرة باستخدام لون معيّن في جزء من الموقع، إلّا أنّ المشكلة هي أنّ ألوانًا كثيرة لن تظهر متناسقة مع اللون الرئيسي لموقعك. تأتي عجلة الألوان للتغلّب على هذه المشكلة، إضافة إلى أنّها تساعد في تحديد الرمز الدقيق للون مّا، بدلًا من تخمينه. Adobe Spark يعمل برنامج Adobe Spark جيّدًا لتصميم البصريّات الاحترافيّة بأنواعها للاستخدام في شبكات التواصل الاجتماعي، أو منصّات التدوين، أو البطاقات المهنيّة، وغيرها. تختصر الأداة العمليّة الطويلة لإنشاء موادّ بصريّة من الصفر على فوتوشوب. كما توفّر قوالب احترافيّة كثيرة للاختيّار بينها. بالمختصر، تمكّن غير المصمّمين من إنشاء موادّ بصريّة جميلة للاستخدام على شبكات التواصل الاجتماعي أو على مواقع الويب دون اللجوء إلى فوتوشوب. ترجمة – بتصرّف – للمقال ‎9 Must-Have Web Design Tools and Alternatives لصاحبه Brenda Stokes Barron.
  6. يشرح هذا المقال كيفيّة إنشاء صور متحرّكة بصيغة GIF انطلاقًا من مجموعة صور بصيغة PNG عبر سطر الأوامر على لينكس (توزيعة أوبونتو). أدناه مثال على تجميع صور لإنشاء صورة متحرّكة تُستخدم لشدّ الانتباه. نبدأ أوّلًا بتثبيت الأداة ImageMagick التي سنستعملها خلال هذا الدرس: sudo apt install imagemagick الخطوة التاليّة لتثبيت الأداة هي الانتقال إلى المجلّد الذي يحوي مجموعة الصوّر التي نريد استخدامها لإنشاء الصورة المتحرّكة. بالنسبة لهذا المثال فمسار المجلّد هوDesktop/shebang : cd Desktop/shebang من السهل جدًّا استخدام ImageMagick وفهمُ آليّة عمله. يوفّر ImageMagick برنامجًا يُسمّى convert، هو الذي سنستخدمه لتحويل الصور. يمكن عبر الأمر التالي إظهار صفحة التوثيق الخاصّة بالبرنامج، والتي تشرح آليّة عمله: man convert تُظهر صفحة التوثيق السطر التالي، والذي يوضح طريقة استخدام البرنامج: convert [input-option] input-file [output-option] output-file نمرّر للبرنامج convert خيّارًا للإدخال، وملفًّا مُدخلًا، ونحدّد خيّارات عمليّة التحويل وأخيرًا اسم الملفّ الناتج عن عمليّة التحويل. نفّذ الأمر التالي في سطر الأوامر: convert -delay 2 -loop 0 *.png -scale 480x270 shebang.gif فلنمرّ على الأمر أعلاه خطوة خطوة. الخيّار الأوّل هو ‎-delay. يعيّن هذا الخيّار مدّة التوقّف بين إطارات الصورة المتحرّكة، بالأجزاء المئويّة من الثانيّة. أعطيناه هنا القيمة 2 (أي جزئيْن مئويّين من ثانيّة). نحدّد بعد ذلك عدد مرات تكرار التحريك عبر الخيّار ‎-loop. نريد ألّا يتوقّف التحريك، لذا نعطي القيمة 0 للخيّار. إنْ أردنا تحريك الصورة لمرّة واحدة فقط، فستكون القيمة 1، وإنْ أردنا تحريكها مرتين فالقيمة المناسبة هي 2. نريد أن يكون مُدخَل البرنامج مجموعةً من الصوّر، لذا نستخدم حرف البدل * لإخبار برنامج convert أنّنا نريد استخدام جميع الصوّر الموجودة في مجلّد العمل التي تنتهي بـpng. لدينا صوّرPNG مُصدَّرة من برنامج Blender، وتبلغ أبعادها ‎1920 x 1080، وهو قيّاس كبير جدًّا ولا يناسب صوّر GIF المتحرّكة، فنحدّد أبعاد الملفّ الناتج عن التحويل عبر الخيّار ‎-scale لتكون الأبعاد ‎.480×270 أخيرًا، ندخل اسم الملفّ الناتج وصيغته (shebang.gif). ترجمة – بتصرّف – للمقال How To Create Animated GIFs from a PNG Sequence with ImageMagick and the Command Line.
  7. ربّما تكون شاشات اللّمس أحد الأسباب التي جعلت الأجهزة الجوّالة ذات شعبيّة كبيرة جدّا. تحذف شاشات اللمس المسافة بين الشخص والجهاز التفاعليّ، ونتيجةً لذلك، يحسّ الأشخاص بأنّ شاشات اللمس بديهيّة وسهلة الاستخدام. لهذا السبب، يمكن إدماج المنزلقات (Sliders) بسهولة في الأجهزة الجوّالة. تشجّع المنزلقات المستخدمين على استكشاف محتوى موقع بسهولة عن طريق إجراء حركات أفقيّة على الشاشة. رغم ذلك، فإنّ المنزلقات خيار قليل الاستخدام للتصفّح على الجوّال. تقدّم المنزلقات خيّار تصفّح رائعًا للمواقع والتطبيقات التي لديها عناصر قليلة لاستكشافها. تبدو المنزلقات طبيعيّة للمستخدمين، وتوفّر طريقة مرحة لتعديل الإعدادات أو استكشاف الميزات. يسهُل على الزوّار التعرّف على المنزلقات، ممّا يجعلها سهلة الاستخدام؛ وبالتالي، خيّارًا عمليًّا جدًّا ليستغلّه المصمّمون. ما لذي تصلُح له المنزلقات المنزلقات سهلة الفهم والاستكشاف، وهو ما يجعلها بديهيّة. تمثّل المنزلقات - نظرًا لكونها لا تأخذ مساحة كبيرة - خيّارًا جيّدًا في الأجهزة التي تستخدم شاشات لمس، التي هي شاشات صغيرة. كما أنّ المنزلقات مناسبة لما تمثّله من إضافة لتصميم واجهات المستخدم. تساعد المنزلقات المستخدمين في التحرّك جيئة وذهابًا، أو زيّادة السرعة أو الصوت، وتمنح حسَّا نسبيًّا للأنشطة (جعل مستوى الصوت أرفع ممّا هو عليه الآن). مصاعب عند استخدام المنزلقات النظر إلى التصميم من زاويّة المستخدم أمر مهمّ في سبيل التأكد من قابليّة الاستخدام (Usability). مستخدمو الجوّال – في الغالب – مستعجِلون أو مشتّتو الانتباه عند استخدام أجهزتهم. يمكن أنْ يستخدموا أجهزتهم وهم يتجوّلون في مركز تسوّق، أو يتناولون الطعام، أو يتنزهون في الحديقة. عندما يمرّ المستخدمون على منزلق، فهم يفعلون ذلك في الغالب مشتّتي الانتباه أو مستعجِلين. يعني هذا غالبًا دفع المنزلق كثيرًا إلى أحد الجانبيْن أثناء محاولة إبعاد أصابعهم عن الشاشة. يمكن أن تكون المنزلقات كذلك صعبة الاستخدام بالنسبة للأشخاص الذين لديهم صعوبات حركيّة. قد يكون من الصعب جعل المنزلق يعمل بالطريقة التي تفضّل أن يعمل بها إذا تلقّى ضغطًا زائدًا قليلًا على الشاشة. قد تكون المنزلقات كذلك صعبة الاستخدام بالنسبة للمستخدمين المتقدّمين في السنّ الذين ترتعش أيديهم؛ وهو ما يجعل ضمان نتيجة محدّدة صعبًا أثناء استخدام المنزلق. ربّما يفقد المستخدمون في مثل هذه الحالات الأمل في إمكانيّة تصفّح الموقع. ضع الجمهور المستهدَف من الموقع بالحسبان. إنْ كنت تستهدف زوّارًا متقدّمين في السن، أو أشخاصًا لديهم صعوبات حركيّة أو صحيّة، فلن يكون المنزلق خيّارك الأمثل. فكّر في مشاكل قابليّة الاستخدام بعد التأكّد من ملاءمة استخدام المنزلقات لجمهورك المستهدَف، يأتي الوقت للتفكير في قابليّة استخدام التصميم. يعتمد مستخدمو شاشات اللمس – غالبًا – الأصابع عند العمل على أجهزتهم. بما أنّك تريد أن يكون تصميمك قابلًا للاستخدام، فمن المهمّ استكشاف أماكن شاشة اللمس التي سيشغلها الأصبع أو اليد عند استخدام الجهاز الجوّال. سيساعد ذلك في وضع المنزلق بحيث يمكن استخدامه والتعامل معه. يختلف التصميم لسطح المكتب، حيث تُستخدَم الفأرة لتحريك المنزلق، عن الأجهزة بشاشات لمس. يجب أن تسعى في حالة شاشة لمس إلى التقليل من إمكانيّة الخطأ، أو الضغط على روابط أخرى، أو تغطيّة التعليمات أثناء وضع المستخدمين أصابعهم على الشاشة. يجب أن تظهر جميع اللافتات (Labels) والقيّم التي تفسّر المنزلق بجانب أصابع المستخدم وزرّ التمرير، أو فوقهما. استخدم المنزلقات لترك انطباع بصريّ واضح يفيد غالبًا استخدام المنزلق لتقديم متتاليّة من الصوّر البصريّة للمستخدمين إذا كان لديك محتوى مهمّ لعرضه، ولكنّ به معلومات كثيرة جدًّا، يصعُب استكشافها أو استيعابها على زوّار الموقع. على سبيل المثال، إنْ رغبت في عرض سلسلة من الأحداث التي يمكن للمستخدم متابعتها بهدف شراء منتَج من موقع تجارة إلكترونيّة، فإنّ مجموعةَ صوّر واضحة تقود المستخدم عبر خطوات متتاليّة يمكن أن تكون مفيدة ولا تتطلّب الكثير من العمل. تقتصد الطريقة السابقة أعلاه من وقت الزائر وتجعل الموقع سهل التصفّح. يمكن استخدام هذه العمليّة في حالات كثرة لمساعدة المستخدم في تصفّح موقع أو تطبيق للجوّال. أنواع المنزلقات التي يمكنك استخدامها توجد أنواع عدّة يمكن استخدامها عند تصميم موقع أو تطبيق: المنزلقات المنفردة (Single sliders): مناسبة للعمل مع قيمة وحيدة في كلّ مرة. المنزلقات المزدوجة (Double sliders): مناسبة للعمل مع مجال من القيم (مثلًا، منزلق للقيمة الدنيا وآخر للقيمة القُصوى). المنزلقات المتواصلة (Continuous sliders): تسمح المنزلقات المتواصلة بتحديد موقع نسبي على مجال مُحدَّد بقيمتين دنيا وقُصوى. المنزلقات المتقطّعة (Discrete sliders): تتميّز بنقاط توقّف يمكن للمستخدم استعمالها لإعطاء قيمة دقيقة. الدقة أم التخمين؟ قد يكون صعبًا أو معقّدًا بالنسبة للزائر استخدامُ منزلق بصورة دقيقة. يتأكّد هذا الأمر على شاشات اللمس. من الصعب جدًّا الحصول على الدقّة في التحديد إذا كنت أمام مؤشّر تمرير صغير على شاشة لمس. يكون استخدام المنزلقات – غالبًا – أسهل عندما لا تكون دقّة القيمة المُختارة مهمّة. إنْ كان المستخدم يستطيع استخدام المنزلق لمشاركة قيمة تقريبيّة، فسيكون ذلك – غالبا – كافيّا. مالعمل إنْ احتجت لقيمة دقيقة؟ يمكن إنشاء منزلق بقيم عدديّة قابلة للتعديل إذا كان من اللازم على المستخدم إدخال قيمة مضبوطة. يعني هذا أنّه سيكون باستطاعة المستخدم لمس المنزلق ثم إدخال عدد في صندوق نصّيّ. يمكن أن يُصبح الصندوق قابلًا للتعديل كلّ ما لمس الأصبعُ المنزلق. رغم ذلك، إنْ كانت واجهة المستخدم تتطلّب قيمة دقيقة من أجل قابليّة الاستخدام، فسيكون من الأفضل عدم استخدام المنزلقات. عرض مجال قِيم تسخدم بعض المنزلقات قيمًا عدديّة بحيث يمكن للزائر تحديد اختيّارات. يمكن لتطبيق تجارة إلكترونيّة أن يوفّر مجموعة من المنتجات تبدأ أسعارها من دولار واحد، لكنّها تصل إلى 999 دولار. يكون من المفيد في هذه الحالة – غالبًا – عرضُ مجال القيّم المتوفّرة على المتجر بدقّة. تمنح هذه الطريقة المستخدم إمكانيّة إنشاء مجال مخصَّص للعناصر التي يريدون استكشافها. تتجنّب بتخصيص مجال من القيّم حصول الزائر على إجابة بمجموعة عناصر فارغة. ستوفّر مجموعة قليلة من قيّم البحث نتيجة بعناصر أقلّ، في حين يوفّر مجال بحث أوسع عناصر أكثر. على الرغم من ذلك، لن يحدّد المستخدمون مجموعةً من القيّم إلّا لسبب، ولن يزعجهم غياب مجموعة من الخيّارات لا يمكنهم دفع ثمنها. أعط للمستخدمين القدرة على استكشاف التطبيق، حتى ولو لم يكونوا يفهمون مغزى المنزلق والمجالات التي يتيحها للمستخدم. وفّر تغذيّة راجعة بصريّة للمستخدمين أضف تغذيّة راجعة بصريّة لتفاعلات المستخدمين مع المنزلق، فتلك هي طريقة العمل التي يتوقّعها المستخدم. وفّر إجابة بصريّة فوريّة للمستخدم عند استخدامه لمنزلق أو عندما يُدخل معلومات في صندوق إدخال. يمكن استخدام التحريكات (Animations)، حالات الحومان (Hover)، والتأشير فوق العناصر (Rollover) للتواصل مع المستخدم. تجذب هذه الميزات انتباه المستخدم وتخلُق بالتالي قناة اتّصال معه. مواقع الويب التفاعليّة جذّابة دائما. عندما تمدّ المستخدم بتغذيّة راجعة بصريّة فإنّه يشعُر بالتقدير، ممّا ينتُج عنه إحساس بالثقة في قدرته على تصفّح الموقع. خاتمة تبدو المنزلقات بديهيّة وسهلة الاستخدام في تطبيقات الويب؛ كما أنّها خيّار جمالي، إلّا أنّها ليست دائمًا الأداة الأفضل للاستخدام. إنْ كنت تبحث عن قيم نسبيّة، وتعلم أنّ الجمهور المستهدَف لديه قدرة حركيّة جيّدة، فإنّ المنزلقات قد تكون خيّارًا جيّدا. أمّا إذا كنت تبحث عن قيّم دقيقة، فسيكون من المفيد توفير خيّارات لإدخال تلك القيّم. اعمل، بوصفك مصمِّمًا، على إنشاء شرائط تمرير يمكن للمستخدم من خلالها اختيار مجال من القيّم يمكن استغلاله على الموقع. يساعد هذا الأمر المستخدمين الذين قد يجدون صعوبة في تحديد قيم دقيقة عند استخدام المنزلق. ترجمة – بتصرّف – للمقال How to Create a Good UI Slider in Mobile Design لصاحبه Bogdan Sandu.
  8. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يعدّ التوقّف عن العمل، أو الإخفاق في تشغيل الخدمة بسبب نقص حجم الذاكرة، أكثرَ أسباب الانهيّارات التي تواجهها MySQL شيوعا. ستحتاج – من أجل التحقّق من الأمر – إلى مراجعة سجل أخطاء MySQL بعد الانهيار. أوّلًا، ابدأ بمحاولة تشغيل خادوم MySQL بكتابة الأمر: sudo systemctl start mysql ثم راجع سجلّات الأخطاء لمعرفة سبب انهيّار MySQL. يمكن استخدام الأمر less لمراجعة السجلّات صفحةً صفحة: sudo less /var/log/mysql/error.log رسالتا الخطأ Out of memory و mmap can’t allocate من الرسائل الشائعة التي تنمّ عن نقص في الذاكرة. في ما يلي بعضٌ من الحلول المحتملة لنقص حجم الذاكرة: تحسين إعدادات MySQL. الأداة مفتوحة المصدر MySQLtuner رائعة لهذا الغرض. يؤدّي تنفيذ سكربت MySQLtuner إلى إظهار مجموعة من التعديلات الموصى بإجرائها على ملف إعدادات MySQL‏ (mysqld.cnf). يُرجى الانتباه إلى أنّه كل ما طالت مدّة تشغيل خادوم MySQL قبل استخدام MySQLTuner كانت اقتراحات السكريبت أحسن. استخدم حاسبة MySQL هذه لتقدير حجم الذاكرة المطلوب بالنسبة لكلّ من إعداداتك الحاليّة وتلك التي يقترحها MySQLtuner. التقليل من اعتماد تطبيق الويب على MySQL في تنزيل الصفحات. إضافة نظام تخزين مؤقّت (Cache) إلى التطبيق هو الوسيلة المعتادة لهذا الأمر. من أمثلة استخدام هذه الطريقة نظامُ Joomla الذي يتضمّن وظيفة تخبئة مُضمَّنة يمكن تفعيلها، والإضافة WP Super Cache التي تضيف خاصيّة التخبئة إلى ووردبريس. الترقيّة إلى خادوم افتراضي ذي قدرات أكبر. يُنصَح بخادوم لا تقلّ ذاكرة الوصول العشوائي فيه عن 1GB مهما كانت نوعيّة استخدام قاعدة البيانات MySQL، إلّا أنّ حجم البيانات ونوعيّتها يمكن أن يؤثر كثيرًا على متطلّبات الذاكرة. يُرجى الانتباه إلى أنّ ترقيّة الخادوم، رغم أنّها من المرجّح أن تحلّ المشكلة، إلّا أنه حلّ لا يُنصَح به إلّا بعد التحقّق من الخيّارات الأخرى ومعرفة احتمالات نجاحها. تكلّف إضافة قدرات أكبر إلى الخادوم سعرًا أعلى ويجدر بك اعتماد هذا الخيّار إلّا إذا كان الحلّ الأمثل في نهاية المطاف. انتبه أيضًا إلى أنّ توثيق MySQL يتضمّن اقتراحات أخرى لتشخيص الانهيّارات ومنع حدوثها. ترجمة – بتصرّف – للمقال How To Address Crashes in MySQL لصاحبه Mark Drake. اقرأ أيضًا المقال التالي: كيف تصلح الجداول المعطوبة في MySQL
  9. ليس لدينا وقتٌ كثير. حتى وأنت تستقطع وقتًا لقراءة هذا المقال، فإنّ إشعارات سطح المكتب بدأت فعلًا في التراكم؛ فترى رسائل سلاك (Slack) تزداد، وتسمع الهاتف يهتزّ؛ ممّا يعني التزاماتٍ أكثر ومهامّ أخرى تحتاج لإضافتها إلى قائمة المهامّ التي لا تنتهي. يتسبّب هذا الكمّ المتزايد من الضّغط في جعل مهامّ مثل تقويم تجربة المستخدم (User eXperience) على موقعك تبدو استغلالًا تافهًا للوقت. بينما الواقع أنّ كلّ ما قمت به من تصميم رسومات، وإنشاء محتوى، وتطوير ويب، وتحسين لمحرّكات البحث، وتسويق عبر شبكات التواصل الاجتماعي، وإعلانات مموَّلة يمكن أن يذهب أدراج الرياح إنْ لم تحسّن تجربة المستخدم. تقع الكثير من الشركات في فخّ التركيز المبالغ فيه على المبيعات وحركة البيانات، دون تدعيم الموقع الذي ترسل إليه الزوّار. السقوط في شَرَك لعبة الأرقام طريقة مؤكَّدة لتحطيم سمعتك الإلكترونيّة، والإضرار بتماسك الهويّة التجاريّة (Brand). ستجد أنّ النجاح في مشهد رقميّ تُخصِّص فيه الكثير من الشركات موارد مهمّة لإرضاء الزوّار أمرٌ صعبٌ جدًّا، إنْ لم تكن تدرك قيمة زوّار موقعك (زبنائك). تدرك – إذن – أهميّة تجربة المستخدم. تدرك كذلك ألّا وقت كثيرًا لديك. كيف يمكنك الرفع لأقصى حد من مستوى الاستفادة من تحليل تجربة المستخدم، في وقت قصير؟ إنْ كنت تعرف هويّتك التجاريّة جيّدًا وتفهم مالذي تبحث عنه، فيمكنك إكمال تقويم تجربة المستخدم في ما لا يتجاوز خمس دقائق. سواءٌ أخذت خمس دقائق مرّةً في الشهر، أو مرّةً في الأسبوع، فتستطيع إجراء تحسينات كبيرة في معدّل الارتداد (Bounce rate)، ومعدّلات التحويل (Conversion rates)، و – عمومًا - العائد على الاستثمار (Return on investment, ROI) من حملاتك التسويقيّة. الجانب البصري (دقيقتان) تشبه ردّةُ فعل شخصٍ يزور موقع ويب لأوّل مرة ردّةَ فعله عند أوّل لقاء بشخص لا يعرفه، إذْ ستنبني ردّة الفعل هذه على الجانب البصريّ. تضع الطبيعة البشريّة وزنًا كبيرًا للانطباع الأوّل، فالبشر يرغبون في الشعور بأنّهم محقّون في حكمهم، حتى وإنْ لم يجدوا الفرصة لإلقاء نظرة أخرى. مهما قيل، فلن يكون ذلك كافيًّا لوصف أهميّة الانطباعات الأولى لزوّار موقع ويب، في ظلّ وجود هذا العدد المَهول من الشركات على الشبكة للاختيّار بينها. يكفي زرّ خارج عن موقعه، أو فقرة بمحاذاة خاطئة، أو خطأ مطبعي، لكي يرتدّ الزوّار إلى موقع منافس. حتى إنّ موقعًا خاليًّا من الأخطاء يمكن أن يتسبّب في انطباع سيّئ إنْ لم يتمسّك بما فيه الكفاية بالهويّة التجاريّة، والهدف العام للرسائل التي يريد تمريرها. خصّص دقيقتيْن لمراجعة الجانب البصري للموقع من وجهة نظر الزائر، وتأكّد من تسجيل أي اختلالات، أو محتوى خارج الإطار المخصّص له، أو ترتيب غير مناسب لمخطّطات الصفحة. ركّز – خصوصًا – على العناصر البصريّة التاليّة عند إجراء تحليل تجربة المستخدم في خمس دقائق: الألوان: هل هي متوافقة مع الهويّة التجاريّة؟ هل اختير الرمز السداسي العشري بما يتوافق مع هدف الفقرة أو الصفحة؟ هل الألوان ناعمة جدًّا أم خشنة جدّا؟ سهولة القراءة: هل الموقع سهلُ القراءة على كلّ من الأجهزة الجوّالة وأجهزة سطح المكتب؟ هل يُميَّز المحتوى المناسب على كلّ صفحة؟ الخطأ في المحاذاة: حتى الاختلالات البسيطة يمكنها إبداءُ الكثير عن عمل شركتك وأخلاقيّاتها. إنْ لم يكن بمقدورك إتقانُ موقعك الخاصّ فكيف ستتمكّن من توفير خدمات أو منتجات جيّدة لزبنائك المحتملين؟ التأثير العامّ: ما مدلول الموقع، أو صفحة منه، من وجهة نظر بصريّة؟ هل يتماشى موقعك ومحتواه مع الشخصيّة التي تعكسها هويّتك التجاريّة؟ التفاعل (دقيقتان) اطمأننتَ على أنّ الانطباع الأوّل للزوّار جيّد. يأتي الدّور الآن على الأمور الأكثر جدّيّة. هل أنت جاهز لإكمال العمل؟ هل أنت جادّ؟ تحتاج لأن تجعل تجربة المستخدم متناسقةً تمامًا من وجهة نظر بصريّة وتفاعليّة، حتى يقتنع الزوّار فعليًّا بما تقدّمه. وفّر لزوّارك العمق الذي ينتظرونه من موقع بميزات بصريّة فريدة. بدون الوظائف المناسبة لتكملة التعديلات البصريّة على الموقع، لن يبقى الزوّار طويلًا، ولن يستمرّوا بالتصفّح إنْ لم يجدوا الإجابات التي يحتاجونها على نحو أسهل أو أسرع من المواقع المنافسة. إنْ كان الجانب الوظيفي من الموقع يحتوي على أخطاء، أو بطيئًا، أو مخادعًا فقد يتسبّب ذلك في إبعاد المستخدِم عن التجربة الغامرة التي تحاول إنشاءها عبر التصميم البصري والمحتوى. خذ الوقت لتقويم العناصر التقنيّة التاليّة بهدف الرفع من مستوى تجربة المستخدم: الأيقونات: هل تلبّي الأيقونات حاجات هويّتك التجاريّة أو المجال الذي تعمل فيه؟ هل الرسومات واضحة ومناسبة للمعايير التي حدّدتها؟ هل يمكن للمستخدمين النقر على الأيقونات للحصول على معلومات أكثر إنْ كانوا يريدون ذلك؟ الأزرار: تأكّد من حصول المستخدمين على نفس القابليّة للتفاعل عبر الموقع، سواءٌ تعلّق الأمر بنصوص الدعوات إلى إجراء (Call-to-action)، أو ألوان الأزرار وأحجامها. الروابط: هل تعمل الروابط جميعًا على النحو المرجو؟ هل تُميَّز الروابط التشعبيّة بنفس اللون، وهل تسهُل رؤيتها؟ النوافذ المنبثقة (Pop-ups) والقوائم المنسدلة (Drop-downs): هل تعمل العناصر البصريّة المُجمَّعة، سواءٌ كانت عناصر قائمة بذاتها أم جزءًا من قائمة، بطريقة مناسبة؟ وهل تندمج في مساحة معيَّنة دون أن تتجاوزها أو تحجُب محتوى مهمًّا آخر؟ المؤشّر: كيف يتغيّر مؤشّر الفأرة عند التفاعل مع الموقع؟ تأكّد أنّ كلّ تغيير متناسق ومتناسب مع الهدف المُراد منه. الزيارة (دقيقة واحدة) تحتاج للقيّام بإجراءات الدقيقة الأخيرة، والانتباه إلى كلّ تفصيل للتأكّد من أن تكون إجابة المستخدم على السؤال هل أنت راضٍ هي "نعم". من المحتمل جدًّا أن يتحوّل الزوّار إلى زبناء، إنْ خصّصت وقتًا لتقويم تجربة المستخدم من الجانب البصري والوظيفي. رغم ذلك، تبقى خطوة أخيرة تتمثّل في تقمّص شخصيّة زوّارك للتأكّد من فاعليّة تجربة المستخدم وجاهزيّتها للرّفع من مداخيلك. يجب أن تسجّل - أثناء النظر في العناصر البصريّة والوظيفيّة - ملاحظات بالتعديلات التي يمكن إجراؤها. إنْ كانت لديك دقيقة إضافيّة فتصفّح موقعك، كما لو كنت زائرًا، منذ لحظة دخولك الموقع وإلى الخروج منه. هل يستطيع زائر جديد تصفّح الموقع بسهولة من أجل العثور على معلومات يحتاجها، أو البحث عن خدمات تقدّمها، أو تقرير شراء منتج؟ فكّر أثناء تحليل زيّارة مستخدم لموقعك، أنّ الموقع عبارة عن جسر وكلّ صفحة عبارة عن مركبة تنقل المستخدمين من جهة (شخص في حاجة لشيء مّا) إلى أخرى (زبون راض). إنْ لم تكن المركبات موجودة، أو كانت غير صالحة، فسيكون احتمال الانتقال من طرف إلى آخر ضئيلًا جدّا. اجعل تصفّح الموقع سلِسًا للمستخدم كلّ ما تعلّق الأمر بعمليّة شراء. الهدف من تجربة المستخدم هو نقل المستخدمين من النقطة "أ" إلى النقطة "ب" بدون المرور عبر حواجز، أو طرق مسدودة، أو حتى تشقّقات. قوّم تجربة المستخدم على موقعك، حتى ولو لم تكن لديك سوى خمس دقائق، من أجل الحصول على تدفّق مستمرّ من الزوّار المهتمين، والزبناء الراضين. ترجمة – بتصرّف – للمقال How to Evaluate Your UX in Under 5 Minutes لصاحبه Kayla Naab.
  10. الحوسبة خفيّة الخوادم (Serverless computing) في طريقها لإحداث ثورة في طرق تطوير البرمجيّات المتعارف عليها. ستساعدك المنصّات المفتوحة المصدر التي نقدّمها هنا في التعرّف على هذا المجال. كثُر الحديث في الآونة الأخيرة عن خفاء الخوادم (Serverless)، فلنوضّح المعنى المقصود بهذا المصطلح، والمصطلحات المرتبطة به، مثل الحوسبة خفيّة الخوادم (Serverless computing) والمنصّات خفيّة الخوادم (Serverless platform). يُستخدَم مصطلح خفاء الخوادم عادةً على أنّه مرادف لتقديم الوظائف بصيغة خدمات (Functions-as-a-Service, FaaS)؛ إلّا أنّ المصطلح لا يعني غيّاب الخادم، عكس ما قد يوحي به الاسم. في الواقع، توجد خوادم عدّة لأنّ مزوّدي الخدمات السحابيّة للعموم يوفّرون خوادم تنشُر، وتشغّل ، وتدير تطبيقك. تعدّ الحوسبة خفيّة الخوادم قسمًا جديدًا يمثّل تحوّلًا في الطريقة التي يبني بها المطوّرون ويوزّعون الأنظمة البرمجية. يمكن أن يؤدّي عزلُ البنية التحتيّة للتطبيقات عن الشفرة البرمجيّة إلى تسهيل عمليّة التطوير مع الحصول على فوائد أخرى من حيث التكلفة والفاعليّة. يرى عدد من المتخصّصين أنّ الحوسبة خفيّة الخوادم وFaaS ستلعبان دورًا أساسيًّا في تحديد أبعاد الحقبة القادمة من تقنيّة المعلومات في المؤسسّات، جنبًا لجنب مع خدمات السحابة الأصيلة Cloud-native والخدمات السحابيّة الهجينة (Hybrid cloud). توفّر المنصّات خفيّة الاسم واجهات تطبيقات برمجيّة (API) تتيح للمستخدمين تشغيل دوالّ برمجيّة (تُسمّى أيضًا إجراءات (Actions)) وتُرجِع نتيجة تشغيل كلّ دالة. توفّر المنصّات خفيّة الخادم كذلك نهايات HTTPS لتمكين المطوّر من الحصول على نتائج الدالّة. يمكن استخدام هذه النهايات (Endpoints) لتكون مُدخلًا Input لدوالّ أخرى؛ وبالتالي توفير متتاليّة (أو سلسلة) من الدوالّ المترابطة. تعمل أغلب المنصّات خفيّة الاسم بحيث ينشُر المستخدم (أو يُنشئ) الدوالّ قبل تنفيذها. يتوفّر لدى المنصّة بعد ذلك كلّ ما يلزم لتنفيذ الشفرة البرمجيّة حالما يُطلب منها ذلك. يُمكن طلب تنفيذ الدالّة خفيّة الخادم يدويًّا بتنفيذ أمر، أو يمكن التسبّب في تنفيذها عبر حدث مرجعي مُعدّ لتنشيط الدالّة للإجابة على أحداث مثل إشعار Cron، وتحميل ملفّ، وأحداث أخرى كثيرة. سبع منصّات مفتوحة المصدر للتعرّف على الحوسبة خفيّة الخوادم Apache OpenWhisk: منصّة مفتوحة المصدر تمكّنك من تنفيذ شفرة برمجيّة استجابةً للأحداث مهما كان عددها. كُتبت المنصّة بلغة Scala، ويمكنها معالجة مُدخلات انطلاقًا من طلبات HTTP ثم تشغيل شفرات برمجيّة مكتوبة بلغة جافاسكريبت أو سويفت Swift. Fission: إطار عمل للحوسبة خفيّة الخوادم يمكّن المطوّرين من إنشاء دوالّ باستخدام Kubernetes. يتيح إطار العمل هذا للمبرمجين كتابة دوالّ تدوم لمدّة قصيرة بأيّة لغة برمجة، وربطها بأي حدث مثل طلبات HTTP. IronFunctions: إطار عمل للحوسبة خفيّة الخوادم يوفّر منصّة للخدمات الصغيرة (Microservices) المترابطة، عن طريق دمج الخدمات الموجودة والاستفادة من Docker. يكتُب المطوّرون الدوالّ بلغة Go. FnProject: منصّة خفيّة الخوادم مُوجَّهة أساسًا للحاويّات يمكن تشغيلها في أي مكان على السحابة أو في البنية التحتيّة الداخليّة. استخدام المنصّة سهل، كما أنّها عاليّة الأداء، و تدعم لغات البرمجة جميعًا، ويمكن توسعتها. OpenLambda: مشروع حوسبة خفيّة الخوادم مُرخص برخصة Apache، ومكتوب بلغة Go، ويعتمد على حاويّات لينكس. يهدف OpenLambda في المقام الأول إلى التمكين من استغلال المقاربات الجديدة للحوسبة خفيّة الخوادم. Kubeless: إطار عمل يعتمد على Kubernetes ويسمح للمطوّر بنشر شفرات برمجيّة قصيرة دون التفكير في البنية التحتيّة المستخدمة. يعتمد Kubeless على موارد Kubernetes لتوفير قابليّة التوسّع الذاتيّة، وتوجيه واجهات التطبيقات البرمجية، والمراقبة، والتشخيص وغيرها. OpenFaas: إطار عمل لبناء دوالّ خفيّة الخوادم باستخدام Docker وKubernates؛ يوفّر دعمًا مدمجًا للقيّاسات والإحصاءات. يُمكن تحزيم أي عمليّة Process على هيئة دالّة، ممّا يسمح باستغلال مجموعة من أحداث الويب دون الحاجة لكتابة شفرات مُكرَّرة. يعدّ Kubernates المنصّة الأكثر انتشارًا لإدارة أحمال العمل في المنصّات خفيّة الخوادم، وحاويّات التطبيقات ذات الخدمات الصغيرة. يستخدم Kubernates نموذج نشر معدًّا بدقّة لمعالجة أحمال العمل بسرعة أكبر وسهولة أكثر. يمكّن Knative Serving من بناء خدمات وتطبيقات خفيّة الخوادم ونشرها على Kubernates، مع استخدام Istio للتوسّع ودعم سيناريوهات عمل متقدّمة، مثل: النشر السريع لحاويات خفيّة الخوادم التوسع والتقلّص التلقائيّيْن (Scaling up and down) التوجيه وبرمجة الشبكات بالنسبة لعانصر Istio أخذ لقطات (Snapshots) من الشفرة المنشورة والإعدادات في نقاط زمنيّة محدّدة. يركّز Knative على المهامّ الاعتيّاديّة من بناءٍ للتطبيقات وتشغيلها على منصّات السحابة الأصليّة (Cloud-native)؛ بهدف تنسيق عمليّات البناء من الشفرة البرمجيّة إلى الحاويّة، وربط الخدمات بأحداث النظام، وتوجيه حركة البيانات وإدارتها أثناء النشر، والتوسّع التلقائي حسب حمل العمل. أمّا Istio، فهو منصّة مفتوحة للاتّصال بالخدمات الصغيرة وتأمينها (وهو في الواقع مستوى تحكّم بنسيج الخدمة Service mesh في الوسيط Envoy) صُمِّم ليتناسب مع تفاعل أشخاص مختلفين مع إطار العمل بما في ذلك المطوّرون، عمّال الصيّانة ومزوّدو الخدمات. يمكن – على سبيل المثال – نشر جافاسكريبت خفيّة الخادم باستخدام Knative Serving على منصّة Minishift محليّة باتّباع الشفرة التاليّة: ## Dockerfile FROM bucharestgold/centos7-s2i-nodejs:10.x WORKDIR /opt/app-root/src COPY package*.json ./ RUN npm install COPY . . EXPOSE 8080 3000 CMD ["npm", "start"] ## package.json { "name": "greeter", "version": "0.0.1", "private": true, "scripts": { "start": "node app.js" }, "dependencies": { "express": "~4.16.0" } } ## app.js var express = require("express"); var app = express(); var msg = (process.env.MESSAGE_PREFIX || "") + "NodeJs::Knative on OpenShift"; app.get("/", function(req, res, next) { res.status(200).send(msg); }); app.listen(8080, function() { console.log("App started in port 8080"); }); ## service.yaml apiVersion: serving.knative.dev/v1alpha1 kind: Service metadata: name: greeter spec: configuration: revisionTemplate: spec: container: image: dev.local/greeter:0.0.1-SNAPSHOT أنشئ تطبيق Node.js وانشره على منصّة Kubernates المحليّة. ستحتاج لتثبيت متطلّبات المنصّة (Knative، و Istio، و Knative Serving على Kubernetes (أو Minishift)). اربط المنصّة بخدمة Docker بالأمر التالي: (minishift docker-env) && eval(minishift oc-env) أنشئ نسخة من حاوية خفيّة الخادم باستخدام Jib عبر الأمر التالي: ./mvnw -DskipTests clean compile jib:dockerBuild انشر خدمة خفيّة الاسم مثل Minishift على عنقود Kubernates بالأمر التالي: kubectl apply -f service.yaml خاتمة يوضّح المثال أعلاه أين وكيف يمكن البدء في تطوير تطبيقات خفيّة الخادم باستخدام منصّات سحابة أصليّة مثل Kubernates، و Knative Serving وIstio. ترجمة - بتصرّف - للمقال 7 open source platforms to get started with serverless computing لصاحبه Daniel Oh.
  11. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL تبدأ الكثير من المواقع والتطبيقات بوضع خادوم الويب وقاعدة البيانات على نفس النظام؛ إلّا أنّ هذا الإعداد يصبح مع الوقت ثقيلًا وغير قابل للتوسيع.. من الشائع التغلّب على هذا الإشكال بفصل الوظيفتيْن بضبط قاعدة بيانات منفصلة ممّا يسمح لخادوم الوِيب وقاعدة البيانات بالنموّ على الأجهزة الخاصّة بها، كلّ حسب حاجته. يعدّ الإعداد المبدئيّ لـ MySQL الذي لا يسمح سوى بالاتّصالات القادمة من نفس الجهاز، وبالتالي يمنع الاتّصالات القادمة من نظام آخر، أحد المشاكل الشائعة التي يواجهها المستخدمون أثناء محاولة إعداد الوصول إلى MySQL عن بعد. يمكن تعديل السلوك المبدئيّ لـ MySQL بتحرير الملف mysqld.cnf: sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf انتقل إلى السطر الذي يبدأ بالتوجيه bind-address. يبدو الملفّ على النحو التالي: . . . lc-messages-dir = /usr/share/mysql skip-external-locking # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 127.0.0.1 . . . يأخذ التوجيه مبدئيًّا القيمة 127.0.0.1، ممّا يعني أنّ الخادوم سيبحث عن الاتّصالات المحليّة فقط. ستحتاج لتعديل هذه القيمة لتكون عنوان IP خارجي (عنوان الجهاز الذي تريد التواصل معه). كما يمكن استخدام حرف بدل (Wildcard) لأغراض التشخيص والبحث عن الأخطاء. مثلًا: *، أو ::، أو 0.0.0.0: . . . lc-messages-dir = /usr/share/mysql skip-external-locking # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 0.0.0.0 . . . ملحوظة: لا يحتوي الملف mysqld.cnf في الإصدار 8 وما بعده من MySQL – مبدئيًّا – على التوجيه bind-address؛ لذا تجب إضافة التوجيه أسفل آخر سطر من الملفّ على النحو التالي: [mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql log-error = /var/log/mysql/error.log bind-address = 0.0.0.0 احفظ الملفّ بعد تعديله، ثم أغلقه وأعد تشغيل خدمة MySQL: sudo systemctl restart mysql حاول بعد ذلك الوصول إلى قاعدة البيانات من جهاز آخر كما يلي (حيث database_server_ip عنوان IP الخاص بخادوم قاعدة البيانات): mysql -u user -h database_server_ip -p إن استطعت الاتّصال بقاعدة البيانات، فهذا يؤكّد أن التوجيه bind-address كان السبب في تعذّر الاتّصال عن بعد بقاعدة البيانات. يُرجى ملاحظة أن القيمة 0.0.0.0 غير آمنة وتتيح الاتّصال بقاعدة البيانات من جميع عناوين IP. أمّا إن لم تستطع الاتّصال بقاعدة البيانات بعد ضبط إعداد bind-address، فهذا يعني أن السبب أمر آخر. قد ترغب - في الحالتين – في التعرّف على كيفيّة إعداد اتّصال بعيد أكثر أمانًا، والتي يشرحها المقال كيفية إعداد قاعدة بيانات بعيدة لتحسين أداء موقع يستخدِم MySQL. ترجمة – بتصرّف – للمقال How To Allow Remote Access to MySQ لصاحبه Mark Drake. اقرأ أيضًا المقال التالي: كيف تعالج انهيار خادوم MySQL
  12. لا يشبه تصميمُ موقع تجارة إلكترونيّة أيّ مشروع ويب آخر، إذ يجب أن تفكّر في الهويّة التجاريّة (Brand) إلى جانب تجربة المستخدم (User experience) والتسويق المعتمد على التصميم. أقلّ ما يُقال هو أنّ الأمر يمكن أن يكون صعبًا. رغم ذلك، توجد مصادر مجانيّة كثيرة يمكن استخدامها لجعل العمل أسهل. مجموعات الأيقونات تعني – بالتأكيد – الكثير من الأشياء لتصميمها. يمكنك اقتصادُ ساعات عملٍ طويلة، وربّما أيّام، باستخدام حزمة أيقونات مجانيّة بدلًا من تصميم أيقونات جديدة. توفّر المجموعات المجانيّة أدناه تعدّدًا في الأنماط، وستجد من خلالها الكثير ممّا يمكن النظر فيه. حزمة 54 أيقونة مجانيّة للتجارة الإلكترونيّة تتضمّن هذه الحزمة الشاملة 54 أيقونة تتميّز بخطوطها البسيطة التي تصلُح للوظائف الشائعة في مواقع التجارة الإلكترونيّة. صمّم Virgil Pana الحزمة ووفّرها مجانًا على موقع Dribbble. تأتي الحزمة كاملةً بصيغة ملفّات PSD، وهي – حقًّا – من أفضل الحزم الموجّهة لمواقع التجارة الإلكترونيّة. خصوصيّة الحزمة في أنّه يمكن تضمينها في أي موقع. النمط التقليلي (Minimalist) مستخدَم كثيرًا، لذا فهو يستحق التجربة، بغض النظر عن المشروع الذي تعمل عليه. مجموعة أيقونات خضراء بسيطة هذه حزمة مجانيّة أخرى موجودة على Dribbble، تركّز على أيقونات خضراء أنيقة. فنيًّا، يمكن تغيير المخطّط اللوني بسهولة عن طريق Illustrator. أنشأ المصمّم Pavel Kozlov هذه الحزمة التي تضمّ 40 أيقونة، وجعلها في المتناول قبل سنوات. رغم ذلك، ما زالت الحزمة قابلةً للاستخدام، تمامًا كما كانت من قبل. زر صفحة Gumroad التاليّة لتنزيل المجموعة. عادةً، يُدفَع مقابل العناصر الموجودة على موقع Gumroad، إلّا أنّ هذه الحزمة مجانيّة بالكامل. يمكن - لمن يرغب في ذلك – إرسال مكافأة صغيرة للمصمّم على جهوده، إلّا أنّه ليس شرطًا للحصول على هذه الأيقونات الخضراء الجذّابة. أيقونات تبضّع (Shopping) متجهيّة مسطَّحة (Flat vector) التصميم المسطَّح (Flat design) توجّه في التصميم يحظى بالشعبيّة. طُبِّق التصميم المسطَّح في كلّ مكان، بدءًا من تطبيقات الجوّال إلى مواقع الويب، بل وحتى الأيقونات. ألق نظرة على هذه المجموعة من أيقونات التبضّع المتجهيّة التي تضمّ 80 أيقونة مختلفة. بعض أيقونات المجموعة ملوَّنة، وأخرى أيقونات بخطوط بسيطة، إلّا أنّها جميعًا سهلة الاستخدام والتخصيص. يمكن تنزيل الحزمة بصيغة PNG أو بصيغتي AI و EPS المتجهيّتيْن، كما أنّه يُسمَح باستخدامها ضمن مشاريع تجاريّة. انتبه إلى أنّ رخصة الأيقونات تطلُب وضعَ رابط إحالة إلى الموقع الأصلي. أيقونات شحن وتجارة إلكترونيّة يتضمّن سوق EpicPxls الإلكتروني موارد عاليّة الجودة. يعدّ هذا السوق مكانًا رائعًا للمصمّمين لينشروا فيه منتجاتهم المجانيّة والمدفوعة، وبالتالي بناء سمعة لأعمالهم. ألق نظرة على مجموعة الأيقونات هذه التي يقدّمها فريق EpicCoders. جاوزت المجموعة أكثر من ألفيْ تنزيل، وهي حقَّا فريدة. بنظرة خاطفة على المجموعة يمكن فهمُ مصدر فرادتها، إذ تبدو نابضة، وشبيهة بالأيقونات الكرتونيّة، لكن دون مبالغة. تناسب الحزمة كثيرًا المواقع التقليليّة (minimalist sites)، إلى جانب أي موقع تجاري يوافق مخطّط ألوان الحزمة. مجموعة أيقونات مرسومة يدويّا قد يكون صعبًا تصميمُ صفحة بعناصر مرسومة يدويّا، إلّا أنها تُضيف لمسة إبداع لكلّ موقع تُضمَّن فيه، وهو ما ينطبق بالتأكيد على هذه الأيقونات المجانيّة. تُركّز هذه الحزمة لأيقونات التجارة الإلكترونيّة بالكامل على العناصر المرسومة يدويّا. توجد جميع الرسومات التي قد تحتاج إليها بدءًا من صناديق الهدايا إلى سلّات التبضّع الصغيرة وميزات تسديد المدفوعات. يُنصَح باستخدام هذه المجموعة أثناء تصميم صفحة هبوط، وليس في واجهات المستخدم الخاصّة بتسديد المدفوعات. يمكن أن تواجه مشاكل في جعل هذه الأيقونات تمتزج طبيعيًّا في واجهات التسديد، لكن يمكنها أن تجلب الانتباه في الصفحة الرئيسيّة لموقع تجاري. حزمة بطاقات دفع لا يكتمل موقع التجارة الإلكترونيّة بدون وجود أيقونات لبطاقات الدفع. إنْ كنت تصمّم متجرًا يقبل بطاقات الدفع الإلكتروني فيجدُر بك إلقاء نظرة على هذه الأيقونات المتاحة للملك العام (رخصة CC0)، والمنشورة على موقع Dribbble. صمّم Guy Levin تسع أيقونات مختلفة لبطاقات دفع، وجمعها كلّها في ملف Illustrator واحد. الأيقونات أنيقة، سهلة التخصيص ويمكنها وضعها في أي مكان من الصفحة. أيقونات خطيّة للتجارة إلكترونيّة نشر موقع Pixlov حزمته الخاصّة من أيقونات مواقع التجارة الإلكترونيّة، وهي حزمة تعتمد على نمط الأيقونات الخطيّة. قد لا تجد أنّ 33 أيقونة عدد كبير، إلّا أنّ تصاميم عدّة توجد في هذه المجموعة للاختيار بينها. لا يُنصَح باستخدام هذه الأيقونات لصفحة تسديد المدفوعات، إذ أنّها مناسبة أكثر لصفحات الهبوط أو صفحات "ميزات المتجر". يمكن أن تناسب المجموعة كذلك النشرة البريديّة وتساعد في زيادة نسبة المبيعات. مشروع Noun للتجارة الإلكترونيّة إنْ لم تكن تعرف مشروع Noun فيجدر بك التعرّف عليه. يجمّع الموقع الكثير من حزم الأيقونات المجانيّة من أماكن مختلفة على الشبكة ومن مصمّمين كثر عبر العالم. يوجد الكثير ممّا يمكن اكتشافه، إلّا أننا في هذا المقال ننصح بمجموعة الأيقونات الخاصّة بالتجارة الإلكترونيّة التي صمّمها Anton Scherbik. هذه المجموعة من الأيقونات من أبسط أيقونات الخطوط، وتتضمّن خيّارات رائعة مثل السلّات والحقائب بأنماط مختلفة. أيقونات الدّفع والتسليم تقوم مدوّنة Codrops بعمل رائع. تنشُر المدوَّنة دوريًّا مقالات عاليّة الجودة تتضمّن موادّ مجانيّة يمكن استخدامها في مشاريعك الخاصّة. من أمثلة ذلك، هذه المجموعة من أيقونات تسديد المدفوعات التي صمّمها EpicPxls. تحوي المجموعة 35 أيقونة فريدة بتأثير حشو لن يمكنك العثور عليه في مكان آخر. تمتد ألوان الحشو دائمًا إلى الخارج قليلًا، وهو أوّل شيئ – ربما – تلاحظه. تقدّم المجموعة بلا شك لمسةً خاصّة. مجموعة أيقونات مُخصَّصة أخيرًا – وليس الأقل أهميّة – هذه المجموعة الرائعة من الأيقونات بواسطة Graphicboat. قد لا تناسب هذه المجموعة جميع الاستخدامات، إلّا أنّها يمكن أن تجذب الانتباه بسرعة وتثير فضول الناس لمعرفة المزيد عن متجرك. يمكن القول إنّ هذه الأيقونات ستتناسب أكثر مع خلفيّة ترويسة، أو في صفحة "ميزات المتجر"، أو ربما في مكان قريب من زرّ الدعوة لإجراء ( CTA). كانت تلك قائمتنا من الأيقونات المجانيّة. إنْ لم تجد ما تبحث عنه، فيمكنك استخدام محرّك بحث للعثور على خيّارات أكثر. تظهر مجموعات مجانيّة جديدة كلّ شهر، لذا يمكن أن تجد ضالتك فيها. ترجمة – بتصرّف – للمقال ‎10 Free Icon Sets for Ecommerce UI Design لصاحبه Jake Rocheleau.
  13. قال الناقد السينمائي روغر إيبر (Roger Eber): "ربّما تتشوّش قدراتك العقليّة مرّة، إلّا أنّ مشاعرك لن تكذب عليك أبدا". تنطبق المقولة السابقة على أبحاث التسويق والدّعاية. إنْ استطعت فهم ردّ الفعل الشعوريّ الحقيقيّ لشخص على مقطع فيديو، أو منتج، أو موقع ويب، أو أيّ محتوى آخر، فسيمكنك التنبّؤ على نحو أدقّ بكيفيّة تحوّل تلك المشاعر إلى أفعال. مالذي يجعل المشاعر بهذه الخصوصيّة؟ تؤكّد دراسات كثيرة على أنّ المشاعر تؤدّي دورًا حيويًّا في قدرتنا على على حفظ المشاعر وتذكّرها. المشاعر هي آليّة بقاء حسب مفهوم نظريّة التطوّر: تحمي مشاعر الخوف من المخاطر في حين تعزّز المشاعر الإيجابيّة سلوك التجرأ على الأخطار، المفيد للبقاء على قيد الحياة. كما أوضح باحثون أنّ البشر فطريًّا أكثر ميلًا إلى الأحداث أو الصوّر الشعوريّة منهم إلى الأحداث أو الصوّر المحايدة. ليس مفاجئًا، من هذا المنطلق، وجودُ اتّجاه تسويقيّ ودعائيّ يجرّب وينشئ محتوى مثيرًا شعوريّا. سواء تعلّق الأمر بالميمة (Meme) التي ألهمت إعلان Sarah McLachlan لمحاربة العنف ضدّ الحيوانات الذي اشتهر بتأثر المشاهدين به حدّ البكاء، أو إعلانات السوبر بول (Super Bowl) الهيستيريّة التي أصبحت حدثًا في ذاتها؛ فإنّ الهويّات التجاريّة التي تثير ردود أفعال شعوريّة جارفة أسهلُ تذكّرًا – بالنسبة اللمشاهدين – كثيرًا من غيرها، وفي آخر المطاف هم أكثر تجاوبًا معها. لكن، كيف يمكننا إثبات الأمر؟ وكيف يمكن – بسرعة - قيّاس التأثير الشعوريّ الذي يحدثه المحتوى؟ استخدمنا أداة Sticky لمعرفة ذلك. تسمح هذه الأداة لفرق التسويق برؤية ما يراه المستخدمون فعليًّا، وفهم ما يشعرون به. وجدنا تلازُمًا (Correlation) بين تذكّر الهويّة التجاريّة وأوج الإجابة الشعوريّة للمشاهدين. تهدف الدراسة إلى معرفة كيف تؤثّر المشاعر ووجود مشاهير في إعلانات العودة إلى المدارس على تذكّر الهويّة التجاريّة لخمس شركات كبرى وهي: Target، و Walmart، و GAP، و Old Navy (تظهر فيه الممثّلة Amy Schumer)، و Old Navy، و Staples. اتّبعنا المنهجيّة التاليّة: استهدفنا 461 مشاركًا تتراوح أعمارهم بين 25 و60 سنة، وهم آباء أو أمّهات يعدّون المنفقين الأساسيّين على عائلاتهم. أرسُلت الدراسة إلى المشاركين عبر الشبكة، وأجروها عن بعد. طُلِب من المشاركين مشاهدةُ واحد من إعلانات العودة إلى المدارس الستة التي تبلغ مدّة كلّ واحد منها 30 ثانيّة، وتتشابه من حيث قدر ظهور الهويّات التجاريّة (الشعار والحوارات). قبل المشاركون استخدام المنصّة لكاميرات أجهزتهم من أجل تتبّع حركات العين وتعابير الوجه خلال الدراسة. سُئل المشاركون ثلاثة أسئلة خلال استبيان تكميلي، بعد مشاهدة مقاطع الفيديو وهي: عن أي شركة يتحدّث الفيديو؟ ما احتمال أن تشتري منتجًا من هذه الشركة لابنك أثناء فترة العودة إلى المدارس؟ ما مدى استحسانك لهذا الفيديو؟. أظهرت النتائج التوجّهات التاليّة: وجود تلازُم بين قدرة المشاركين على تذكّر الهويّة التجاريّة في الإعلان وأوج الإجابة الشعوريّة للمشاهدين. نتج عن مقاطع الفيديو ذات الأوج الشعوريّ المتشابه نسب تذكّر متقاربة (Walmart و Target). أدّى إشراك شخصيّة مشهورة إلى إحداث دفعة لتذكّر الهويّة التجاريّة (Old Navy مقابل Staples). ليس ضروريًّا أن يثير الإعلان مشاعر إيجابيّة ليكون له تأثير على تذكّر الهويّة التجاريّة (أثار إعلان GAP "الاشمئزاز" في أوج الإجابة الشعوريّة). الأوج الشعوري (Peak emotion) هو النقطة التي عبّر عندها أغلب المشاركين عن نفس الشعور. تذكّر الهويّة التجاريّة (Brand recall) هو التذكّر غير الموجّه للهويّة التجاريّة المعروضة في الإعلان. تأثير الضحك يوجد أدناه مقطع من إعلان Staples في فترة العودة إلى المدارس، الذي يبرز أغنيّة عيد الميلاد الشهيرة "إنّها أجمل أيّام السنة" (It’s The Most Wonderful Time of the Year). يُظهر الإعلان أبًا يرقُص بفرح في ممرّات المتجر في حين يتثاقل أبناؤه خلفه، يظهر عليهم الإحباط والفتور. ليس مفاجئًا أن تكون ردّة فعل الآباء إيجابيّة على الفيديو الذي يجمع بين ما قد يشعر به الآباء من تحمّس لعودة أبنائهم للمدارس، وفتور الأطفال. يظهر الأوج الشعوري مبكّرًا في الإعلان، مع أوج شعوري قيمته 9% بعد 8 ثوان فقط، ممّا يؤثّر على مشاعر المشاهدين ويؤدّي إلى نسبة تذكّر تبلغ 91%. لاحظ في المقطع المُصوّر أعلاه كيف يتغير مركز الانتباه خلال إعلان Staple. تأثير المشاهير توجد أدناه صورة متحرّكة لإعلان Old Navy الذي تظهر فيه الممثّلة Amy Schumer. استخدم الإعلان الكوميديا للوصول إلى المشاهد، ليس هذا فقط، بل إنّ 89% من المشاهدين تتبّعوا الممثّلة خلال مقطع الفيديو. حدث ذلك كثيرًا عندما تكون الممثّلة داخل إطار الصورة، ممّا يؤكّد أن شهرتها كانت سببًا أساسيًّا للفت الانتباه البصري. تتماشى هذه الملاحظة مع التوقّع بأنّ إبراز شخصيّة مشهورة يُشعِر بالثقة والألفة تُجاه الهويّة التجاريّة، ممّا يزيد من احتمالات تذكّرها، وبالتالي نيّات الشراء. رغم أنّ إعلان Staples حقّق أوجًا شعوريًّا أعلى، إلّا أنّ حضور Amy Schumer منح إعلان Old Navy دفعة لتذكّر تمكن ملاحظتها في النتائج (96 % مقابل 91 %). كان بمقدورنا كذلك ملاحظة تأثير الشهرة بالتمعّن في إعلانيْن من Old Navy، أحدهما توجد فيه شخصيّة مشهورة، وأخرى لا توجد به. بوجود الشخصيّة المشهورة أدّى الأوج الشعوريّ الأعلى عند 7.5 % ووجود الشخصيّة إلى الوصول لنسبة تذكّر بلغت 96 %. في غيّاب الشخصيّة المشهورة، أدّى الأوج الشعوريّ الأدنى قليلًا (7 %) إلى نسبة تذكّر بلغت 86 %. يُظهرالمقطع المُصوّر أعلاه كيف يتغير مركز الانتباه خلال إعلان Old Navy بوجود الممثّلة. لقطة شاشة تظهر الانتباه البصري الأعلى (اللون الأبيض والأزرق) ضمن منطقة الانتباه. يمكننا الاستنتاج، انطلاقًا من هذه الدراسة، أنّ الجمع بين إثارة شعوريّة معتبرة، وحضور شخصيّة مشهورة يمكن أن يرفع من إمكانيّة تذكّر الهويّة التجاريّة إلى الحدّ الأقصى، وبالتالي احتمال شراء منتجاتها. وفّرت الدراسة إثباتات حيويّة على هذه المبادئ الدعاية المتعارف عليها. على الرغم من أنّ النتائج المعروضة هنا ليست ثوريّة، إلّا أنّها تذكير جيّد لكلّ مسوِّق، أو مُعلن، أو مدير تجاري أو باحث تسويق أنّه يتعامل مع كائنات بشريّة تتفاعل مع المحتوى طوال اليوم على الشاشات وفي الواقع الملموس؛ سواء تعلّق الأمر باللوحات الإعلانيّة في الشوارع، أو إعلانات إنستاغرام المُوجَّهة، أو الإعلانات التجاريّة على التلفزيون أو غيرها. لا يتذكّر الناس أشياء، وبالتالي يتجاوبون معها، إلّا إذا أثارت لديهم إجابة شعورية. ترجمة – بتصرّف – للمقال Leveraging emotions and celebrity to increase brand recall لصاحبه Joey Goldberg.
  14. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. فهرس مقالات السلسلة: كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيّار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يمكن في كثير من الأحيان التعرّف على السبب وراء بطء، أو انهيار، أو أي سلوك غير متوقّع من قاعدة قاعدة البيانات MySQL بتحليل سجلات الأخطاء. توجد هذه السجلات مبدئيًّا في المسار ‎/var/log/mysql/error.log‎، بالنسبة للخواديم التي تعمل بتوزيعة أوبونتو. يعدّ برنامج less، وهو أداة تعمل على سطر الأوامر تتيح قراءة الملفّات دون إمكانيّة تعديلها، الوسيلة الأسهل في كثير من الأحيان، لقراءة سجلات الأخطاء: $ sudo less /var/log/mysql/error.log يمكن الحصول على معلومات أكثر عن مصدر المشاكل في حال سلوك غير متوقّع من MySQL عبر تنفيذ الأمر أعلاه وتشخيص الخطأ بناءً على محتويات السجلّات. تشخيص استعلامات MySQL قد يواجه المستخدمون، فور بدئهم في تنفيذ الاستعلامات Queries، مشاكل بطء في قاعدة بيانات MySQL. يجب عند تنفيذ استعلامات في بعض من أنظمة قواعد البيانات، بما فيها MySQL، إنهاء الاستعلام بفاصلة منقوطة (;) من أجل إتمام الاستعلام؛ كما في المثال التالي : SHOW * FROM table_name; إن لم تضف فاصلة منقوطة في نهاية الاستعلام، فإن سطر الأوامر سيستمر في الانتقال إلى سطر جديد ما لم تكتب الفاصلة المنقوطة وتضغط بعدها على زر الإدخال Enter. قد يجد بعضٌ من المستخدمين أن استعلاماتهم بطيئة إلى حد بعيد. يعدّ تمكين سجلّ الاستعلامات البطيئة في MySQL ومراجعته. إحدى الطرق الناجعة لمعرفة سبب البطء. لفعل ذلك، افتح الملف mysqld.cnf الذي يُستخدَم لضبط خيارات خادوم MySQL. يوجد هذا الملف عادةً في المسار ‎/etc/mysql/mysql.conf.d/‎ : sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf تَنقّل في الملف إلى أن تبلغ الأسطر التالية : . . . #slow_query_log = 1 #slow_query_log_file = /var/log/mysql/mysql-slow.log #long_query_time = 2 #log-queries-not-using-indexes . . . تضبُط التوجيهات المُعلّقة أعلاه (توجد علامة # قبلها) خيّارات الإعداد المبدئيّة لسجلّ الاستعلامات البطيئة. في ما يلي شرحٌ لعمل كلّ منها : slow-query-log: إعطاء القيمة 1 لهذا التوجيه يفعّل سجلّ الاستعلامات البطيئة. Slow-query-log-file: يعرّف الملفّ الذي سيسجّل فيه MySQL الاستعلامات البطيئة. يشير التوجيه في المثال أعلاه إلى الملف ‎/var/log/mysql-slow.log. long_query_time : يؤدي إعطاء القيمة 2 لهذا التوجيه إلى ضبط MySQL لتسجيل جميع الاستعلامات التي تتجاوز مدة تنفيذها الثانيتين. log_queries_not_using_indexes : يخبر MySQL أن يسجّل أي استعلام لا يستخدم فهارس Indexes ضمن الملف ‎/var/log/mysql-slow.log. ليس مفروضًا استخدامُ هذا الإعداد لكي يعمل سجل الاستعلامات البطيئة إلا أنه مفيد جدًّا في التعرّف على الاستعلامات غير الفعّالة. انزع تعليق الأسطُر بحذف علامة # من أوّلها. سيبدو المقطع أعلاه على النحو التالي بعد نزع التعليقات: . . . slow_query_log = 1 slow_query_log_file = /var/log/mysql-slow.log long_query_time = 2 log_queries_not_using_indexes . . . ملحوظة: إن كنت تسخدم الإصدار 8 وما تلاه من MySQL، فلن يحتوي الملف mysqld.cnf مبدئيًّا على الأسطُر المعلّقة التي تحدّثنا عنها آنفًا، لذا أضف توجيهات الإعداد أسفل الملف على النحو التالي : . . . slow_query_log = 1 slow_query_log_file = /var/log/mysql-slow.log long_query_time = 2 log_queries_not_using_indexes احفظ الملف بعد تمكين سجلّ الاستعلامات البطيئة ثم أغلقه. أعد تشغيل خدمة MySQL كما يلي : sudo systemctl restart mysql سيمكن بعد هذه الإعدادات العثور على الاستعلامات التي تسبّب مشاكل بطء الخادوم. استخدم الأمر less لعرض الملف كما يلي : sudo less /var/log/mysql_slow.log الخطوة التالية بعد فرز الاستعلامات البطيئة هي البحث عن طريقة لتحسينها؛ مقال كيف تحسّن الاستعلامات والجداول في MySQL و MariaDB مفيد لهذا الغرض. علاوة على ذلك؛ يتضمّن MySQL التعليمة EXPLAIN التي توفّر معلومات عن كيفيّة تنفيذ MySQL للاستعلامات. تقدّم هذه الصفحة من التوثيق الرسمي لـ MySQL أفكارًا حول كيفيّة استخدام EXPLAIN لتسليط الضوء على الاستعلامات غير الفعّالة. راجع المقال أساسيّات لغة SQL من أجل التعرّف على البنية الأساسيّة لتعليمات MySQL. ترجمة – بتصرّف – للمقالين How to Access MySQL Error Logs و How To Troubleshoot MySQL Queries لصاحبهما Mark Drake. اقرأ أيضًا المقال التالي: كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL
  15. مقدّمة نظرية كائن جافاسكريبت هو كيان لديه خاصيّات. كلّ خاصيّة عبارة عن زوج مفتاح وقيمة. المفتاح هو اسم الخاصيّة. يمكن أن تكون قيمة الخاصيّة بيانات (عددا، سلسلة محارف، …إلخ.) أو دالة. يُطلَق على الخاصيّة عندما تكون قيمتها دالة الاسم تابع Method. يُنشَأ كائن حرفي Object literal في جافاسكريبت بتحديد خاصيّاته ضمن زوج من الأقواس المعكوفة. const myObject = { property1: value1, property2: value2, // ... , method1(/* ... */) { // ... }, method2(/* ... */) { // ... } // ... }; myObject.property1 = newValue; // يعيّن القيمة الجديدة للخاصيّة property1 في الكائن myObject console.log(myObject.property1); // يعرض قيمة الخاصيّة property1 في الكائن myObject myObject.method1(...); // استدعاء التابع method1 في myObject تمثّل الكلمة المفتاحية this في تابع الكائن الذي يُستدعَى فيه التابع. تعرّف لغة البرمجة سلفا كائنات عدّة للاستفادة منها مثل console و Math. مقدّمة ما هو الكائن؟ انظر إلى الكائنات في معناها غير البرمجي، مثل قلم. يمكن أن يكون للقلم ألوان عدّة، يصنعه أشخاص متعدّدون، أطراف متنوّعة وخاصيّات أخرى كثيرة. على نحو مشابه، الكائن في البرمجة هو كيان لديه خاصيّات. تعرّف كل خاصيّة ميزة في الكائن. يمكن أن تكون الخاصيّة بيانات مرتبطة بالكائن (لون القلم) أو إجراء (قدرة القلم على الكتابة). ما علاقة هذا بالشفرة؟ البرمجة كائنية التوجّه Object-oriented programming (أو OOP اختصارا) هي طريقة لكتابة البرامج باستخدام الكائنات. يكتُب المبرمج - عند اتّباع هذه الطريقة - الكائنات، ينشئها ويعدّل عليها؛ تشكلّ الكائنات البرنامج. ** تغيّر البرمجة كائنية التوجّه الطريقة التي تُكتَب وتُنظَّم بها البرامج. كتبنا في الفصول السابقة برامج تعتمد على الدوالّ، وهي طريقة برمجيّة تُسمَّى البرمجة الإجرائية Procedural programming. فلنكتشف الآن كيف نكتب شفرة كائنية التوجّه. جافاسكريبت والكائنات تدعم جافاسكريبت، مثل لغات برمجة أخرى، البرمجة بالكائنات. كما توفّر كائنات معرَّفة مسبقا مع إتاحة الفرصة لإنشاء كائنات جديدة. إنشاء كائن في ما يلي تمثيل جافاسكريبت لقلم حبر جاف أزرق اللون علامته التجارية Bic. const pen = { type: "حبر جاف", color: "أزرق", brand: "Bic" }; يمكن إنشاء كائنات جافاسكريبت، كما ذكرنا سابقا، بسهولة بتعيين خاصيّات الكائن ضمن زوج أقواس معكوفة {...}. كلّ خاصيّة هي زوج من المفاتيح والقيم. يُسمَّى الكائن المعرَّف سابقا بالكائن الحَرْفي Object literal. ملحوظة: النقطة الفاصلة ; بعد زوج الأقواس اختيارية، إلا أنه من الآمن إضافتها على كلّ حال. تعرِّف الشفرةُ أعلاه متغيّرا يُسمَّى pen قيمته كائن، يمكننا القول إذن إن pen كائن. لهذا الكائن ثلاث خاصيّات هي: type (النوع)، color (اللون) وbrand (العلامة التجارية). لكلّ خاصيّة اسمٌ وقيمة، كما أنها متبوعة بفاصلة لاتينية , (ما عدا الخاصيّة الأخيرة) الوصول إلى خاصيّات الكائن يمكن الوصول إلى قيم الخاصيّات بعد إنشاء الكائن بالتنويت النقطي Dot notation مثل myObject.myProperty. const pen = { type: "حبر جاف", color: "أزرق", brand: "Bic" }; console.log(pen.type); // "حبر جاف" console.log(pen.color); // "أزرق" console.log(pen.brand); // "Bic" الوصول إلى خاصيّة كائن هو عبارة Expression تنتج قيمة. يمكن تضمين هذه العبارة في عبارات أكثر تعقيدا. يوضّح المثال التالي كيفية عرض خاصيّات القلم السابق في تعليمة واحدة: const pen = { type: "حبر جاف", color: "أزرق", brand: "Bic" }; console.log(`أكتب بقلم ${pen.type} لونه ${pen.color} وعلامته التجارية ${pen.brand}`); التعديل على كائن يمكن تعديل قيم الخاصيّات في كائن بعد إنشائه بالصيغة myObject.myProperty = newValue. const pen = { type: "حبر جاف", color: "أزرق", brand: "Bic" }; pen.color = "أحمر"; // تغيير لون القلم console.log(`أكتب بقلم ${pen.type} لونه ${pen.color} وعلامته التجارية ${pen.brand}`); توفّر جافاسكريبت إمكانية الإضافة الديناميكية لخاصيّات جديدة لكائن أنشأته قبْلا: const pen = { type: "حبر جاف", color: "أزرق", brand: "Bic" }; pen.price = "2.5"; // تعيين خاصية لسعر القلم console.log(`يبلغ سعر قلمي ${pen.price}`); البرمجة بالكائنات تعلّم الكثير من الكتب والدورات البرمجة كائنية التوجّه عبر أمثلة عن الحيوانات، السيّارات أو الحسابات المصرفية. فلنجرّب أمرا ألطف ولننشئ لعبة تقمّص أدوار Role playing game مصغَّرة باستخدام الكائنات. تُعرَّف كلّ شخصية في ألعاب تقمّص اﻷدوار بصفات مميَّزة عدّة مثل القوّة، القدرة على التحمّل والذكاء. في ما يلي لقطة شاشة للعبة تقمّص أدوار شهيرة على الإنترنت. سيكون للشخصيّات - في مثالنا الأبسط كثيرا - ثلاثُ صفات مميّزة: الاسم Name، الصّحة Health (عدد نقاط الحياة)، القوة Strength. مثال ساذج فلنقدّم أورورا، الشخصيّة الأولى في لعبتنا لتقمّص الأدوار: const aurora = { name: "أورورا", health: 150, strength: 25 }; للكائن aurora ثلاث خاصيّات: health، name وstrength. ملاحظة: يمكن - كما ترى في المثال أعلاه - إسناد أعداد، سلاسل محارف وحتى كائنات أخرى إلى خاصيّات الكائنات. تستعدّ أورورا للبدء في سلسلة من المغامرات العظيمة التي ستحدّث بعض منها خاصيّات الشخصيّة. تأمل المثال التالي: const aurora = { name: "أورورا", health: 150, strength: 25 }; console.log(`يوجد لدى ${aurora.name} نقاط قوة قدرها ${aurora.health} وقوة تبلغ ${aurora.strength}`); // أصاب سهم أورورا وبالتالي تقل نقاط الحياة aurora.health -= 20; // تتجهّز أورورا بقلادة قوة aurora.strength += 10; console.log(`يوجد لدى ${aurora.name} نقاط قوة قدرها ${aurora.health} وقوة تبلغ ${aurora.strength}`); التعريف بالتوابع احتجنا في الأمثلة السابقة إلى كتابة تعليمات console.log طويلة في كلّ مرة نريد عرض حالة الشخصية. توجد طريقة أنسب للوصول إلى هذا الغرض. إضافة تابع لكائن تأمل المثال التالي: const aurora = { name: "أورورا", health: 150, strength: 25 }; // ترجع وصف الشخصية function describe(character) { return `يوجد لدى ${character.name} نقاط صحة قدرها ${character.health} وقوة تبلغ ${character.strength}`; } console.log(describe(aurora)); معامل الدالة describe() هو كائن. تصل الدالة إلى خاصيّات الكائن وتنشئ سلسلة المحارف التي تصف الشخصية. أدناه مقاربة بديلة تستخدم الدالة describe() داخل الكائن. const aurora = { name: "أورورا", health: 150, strength: 25, // ترجع وصف الشخصية function describe(character) { return `يوجد لدى ${character.name} نقاط صحة قدرها ${character.health} وقوة تبلغ ${character.strength}`; } }; console.log(aurora.describe()); يتوفّر الكائن الآن على خاصيّة جديدة: describe(). قيمة هذه الخاصيّة دالة تُرجِع وصفا نصيًّا للكائن. نتيجة التنفيذ مطابقة تماما لما سبق. تُسمّى خاصيّة كائن عندما تكون قيمتها دالة بالتابع. تُستخدَم التوابع لتعريف إجراءات على كائن. يضيف التابع سلوكا إلى الكائن. استدعاء تابع في كائن فلنتأمل السطر الأخير من المثال السابق: console.log(aurora.describe()); نستخدم العبارة aurora.describe() لعرض وصف الشخصية بدلا من describe(aurora)، وهنا فرق جوهري. تستدعي العبارة describe(aurora) الدالة describe() مع تمرير الكائن aurora في المعطيات. الدالة خارجة عن الكائن. هذا مثال على البرمجة الإجرائية. تستدعي العبارة aurora.describe() الدالة describe() في الكائن aurora. الدالة خاصيّة من خاصيّات الكائن: تابع. هذا مثال على البرمجة كائنية التوجه. صيغة استدعاء التابع myMethod() في myObject هي myObject.myMethod(). تنبيه: تذكّر الأقواس، حتى وإن كانت خاوية، عند استدعاء تابع. الكلمة المفتاحية this تأمل جيّدا متن التابع describe() في المثال التالي: const aurora = { name: "أورورا", health: 150, strength: 25, // ترجع وصف الشخصية describe() { return `يوجد لدى ${this.name} نقاط صحة قدرها ${this.health} وقوة تبلغ ${this.strength}`; } }; سترى كلمة مفتاحية جديدة: this. تُعيَّن هذه الكلمة المفتاحية تلقائيا داخل تابع في جافاسكريبت وتمثّل الكائن الذي استُدعِي فيه التابع. لا يستقبل التابع describe() أي معاملات. يستخدم التابع this للوصول إلى خاصيّات الكائن الذي استُدعِي فيه. الكائنات المعرَّفة مسبقا في جافاسكريبت تتوفّر جافاسكريبت على كائنات عدّة معرَّفة مسبقا تخدم أغراضا متفرّقة. رأينا في ما مضى بعضا منها: يمنح الكائن console الوصول إلى بيئة الطرفية. التعليمة console.log() هي في الواقع استدعاء لتابع. يحوي الكائن Math خاصيّات رياضية كثيرة. على سبيل المثال، تُرجع الخاصيّة Math.PI قيمة تقريبية للعدد π، ويرجع التابع Math.random() عددا عشوائيا بين 0 و1. حان وقت كتابة الشفرة! إضافة تجربة الشخصية حسّن برنامج لعبة تقمّص الأدوار بإضافة خاصيّة التجربة؛ على أن يكون اسمها xp وقيمتها الابتدائية 0. يجب أن تظهر التجربة ضمن وصف الشخصية. // للإنجاز: إنشاء الكائن character هنا. // أصاب سهم أورورا وبالتالي تقل نقاط الحياة aurora.health -= 20; // تتجهّز أورورا بقلادة قوة aurora.strength += 10; // تعلّمت أورورا مهارة جديدة aurora.xp += 15; console.log(aurora.describe()); نمذجة Modeling كلب أكمل البرنامج التالي لإضافة تعريف بالكائن dog (كلب). // للإنجاز: أنشئ الكائن dog هنا console.log(`${dog.name} كلب نوعه ${dog.species} يبلغ طوله ${dog.size}`); console.log(`انظر هرّة! ${dog.name} ينبح: ${dog.bark()}`); نمذجة دائرة أكمل البرنامج التالي لإضافة تعريف الكائن circle (دائرة). يُدخل الزائر قيمة شعاع الدائرة const r = Number(prompt("أدخل قيمة شعاع الدائرة:")); // للإنجاز: أنشئ تعريف الدائرة هنا console.log(`يبلغ محيط الدائرة ${circle.circumference()}`); console.log(`تبلغ مساحة الدائرة ${circle.area()}`); نمذجة حساب مصرفي أنشئ برنامجا ينشئ كائن account يمثّل حسابا مصرفيا لديه الميزات التالية: خاصيّة name قيمتها “أحمد”. خاصيّة balance قيمتها 0. تابع credit تضيف القيمة (سالبة أو موجبة) المُمرَّرة في المعطى إلى رصيد الحساب balance. تابع describe يُرجع وصف الحساب. استخدم هذا الكائن لعرض وصف حساب مصرفي، أضف 250 إلى رصيده، اسحب منه 80 ثم اعرض وصفه مرة أخرى. تحصُل على الآتي عند عرض وصف البرنامج في المرة الأولى: “المالك: أحمد، الرصيد: 0” وعلى ما يلي في المرة الثانية: “المالك: أحمد، الرصيد: 170” ترجمة - بتصرّف - للفصل Create your first objects من كتاب The JS Way.
  16. لغة C# هي لغة برمجة أنيقة، كائنيّة التوجه Object-oriented بأنواع بيانات سليمة Type-safe تمكّن المطورين من بناء تطبيقات آمنة ومتينة تعمل على إطار العمل NET. // تبدأ التعليقات وحيدة السطر بشريطين مائلين هكذا // /* توضع التعليقات متعدّدة الأسطر بين العلامة أعلاه والعلامة أسفله */ هذا تعليق xml يُستخدَم لتوليد توثيق خارجي أو لتقديم مساعدة حسب السياق في بيئة تطوير مندمجة IDE. /// <param name="firstParam"> لتوثيق الدالة Parameter الذي هو معامل firstParam هذا تعليق</param> /// <returns>معلومات عن القيمة المُرجَعة للدالة/returns> //public void MethodOrClassOrOtherWithParsableHelp(string firstParam) {} يحدّد فضاءات اﻷسماء Namespaces التي ستستخدمها هذه الشفرة فضاءات الأسماء أدناه هي كلّها جزء من مكتبة الأصناف Classes المعيارية في إطار العمل NET. Framework Class Library using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Net; using System.Threading.Tasks; using System.IO; فضاء الأسماء هذا ليس مُتضمّنا في المكتبة المعيارية: using System.Data.Entity; لكي تتمكّن من استخدام المكتبة أعلاه فستحتاج لإضافة مرجع إلى ملف dll وهو ما يمكن لمدير الحزم NuGet فعلُه: Install-Package EntityFramework تعرّف فضاءات الأسماء مجالات لتنظيم الشفرات ضمن حزم Packages أو وِحْدات Modules لاستخدام فضاء الأسماء المُعرّف أدناه في شفرة أخرى نضيف العبارة Learning.CSharp إلى فضاءات الأسماء المستخدمة namespace Learning.CSharp { يجب أن يحوي كل ملف cs. على الأقل على صنف Class له نفس اسم الملف. يمكنك لك عدم التقيّد بهذا الشرط، إلا أنه أفضل لصحة الشفرة المصدرية public class LearnCSharp { صياغة أساسية: يمكنك التجاوز إلى “ميزات مثيرة للاهتمام” إن سبق لك كتابة شفرات بجافا أو سي++ public static void Syntax() { // للكتابة في سطر جديد Console.WriteLine استخدم Console.WriteLine("Hello World"); Console.WriteLine( "Integer: " + 10 + " Double: " + 3.14 + " Boolean: " + true); // لكتابة عبارات على نفس السطر Console.Write استخدم Console.Write("Hello "); Console.Write("World"); أنواع البيانات Types والمتغيّرات Variables دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن عرّف المتغيّرات على النحو التالي <type> <name> Sbyte - عدد صحيح (سالب أو موجب) على 8 بتات (محصور بين 128- و127) sbyte fooSbyte = 100; Byte - عدد طبيعي (موجب فقط) على 8 بتات (محصور بين 0 و255) byte fooByte = 100; Short - عدد صحيح أو طبيعي طوله 16 بتات صحيح short محصور بين -32,768 و32,767 طبيعي ushort محصور بين 0 و65,535 short fooShort = 10000; ushort fooUshort = 10000; عدد صحيح fooInt أو طبيعي fooUint طوله 32 بت int fooInt = 1; // (-2,147,483,648 <= int <= 2,147,483,647) uint fooUint = 1; // (0 <= uint <= 4,294,967,295) Long عدد صحيح fooLong أو طبيعي fooUlong طوله 64 بت long fooLong = 100000L; // (-9,223,372,036,854,775,808 <= long <= 9,223,372,036,854,775,807) ulong fooUlong = 100000L; // (0 <= ulong <= 18,446,744,073,709,551,615) النوع المبدئي default للأعداد هو int أو uint حسب طول العدد. والحرف L وراء العدد يشير إلى أن نوع العدد هو long أو ulong Double - فاصلة عائمة مزدوجة الدقة حسب المعيار 64-bit IEEE 754 double fooDouble = 123.4; // الدقة: 15-16 رقما Float - فاصلة عائمة وحيدة الدقة 32-bit IEEE 754 Floating Point float fooFloat = 234.5f; // الدقة: 7 أرقام يشير الحرف f وراء العدد إلى أن نوع العدد هو Float Decimal - نوع بيانات بطول 128 بت، ودقّة أعلى من بقية أنواع البيانات ذات الفاصلة العائمة مناسب للحسابات المالية والنقدية decimal fooDecimal = 150.3m; // Boolean - true & false bool fooBoolean = true; // or false Char - نوع بيانات بطول 16 بت يرمز لمحرف يونيكود `char fooChar = 'A'; Strings – على النقيض من جميع أنواع البيانات السابقة التي هي أنواع لقيم البيانات فإن النوع String - سلسلة محارف - هو نوع لمرجع Reference بمعنى أنه يمكنه أخذ القيمة null string fooString = "\"escape\" quotes and add \n (new lines) and \t (tabs)"; Console.WriteLine(fooString); يمكن الوصول إلى كل محرف من سلسلة المحارف عن طريق ترتيبه في السلسلة char charFromString = fooString[1]; // => 'e' لا يمكن التعديل على سلاسل المحارف؛ التعليمة fooString[1] = X خاطئة مقارنة سلاسل محارف مع قيمة الخاصيّة CurrentCulture المعرّفة في المكتبة المعيارية لتمثيل اللغة المستخدمة في النظام، مع تجاهل حالة الأحرف IgnoreCase string.Compare(fooString, "x", StringComparison.CurrentCultureIgnoreCase); تهيئة سلسلة المحارف اعتمادا على sprintf string fooFs = string.Format("Check Check, {0} {1}, {0} {1:0.0}", 1, 2); التاريخ والتهيئة DateTime fooDate = DateTime.Now; Console.WriteLine(fooDate.ToString("hh:mm, dd MMM yyyy")); سلاسل المحارف الأصلية Verbatim String يمكنك استخدام العلامة @ أمام سلسلة محارف لتخليص جميع المحارف الموجودة في السلسلة string path = "C:\\Users\\User\\Desktop"; string verbatimPath = @"C:\Users\User\Desktop"; Console.WriteLine(path == verbatimPath); // => true يمكنك توزيع سلسلة محارف على أكثر من سطر بالرمز @ لتخليص العلامة " ضع مكانها "" (" مرتين) string bazString = @"Here's some stuff on a new line! ""Wow!"", the masses cried"; استخدم الكلمة المفتاحية const لجعل المتغيّر ثابتًا غير قابل للتعديل وتُحسب القيم الثابتة أثناء تصريف البرنامج compile time const int HoursWorkPerWeek = 9001; بنى البيانات المصفوفات - يبدأ العنصر الأول عند الترتيب 0 ويجب تحديد قياس المصفوفة عند تعريفها صيغة تعريف المصفوفة هي كالتالي: ;<datatype>[] <var name> = new <datatype>[<array size>] المصفوفة intArray في المثال التالي تحوي 10 أعداد int[] intArray = new int[10]; طريقة أخرى لتعريف مصفوفة وتهيئتها int[] y = { 9000, 1000, 1337 }; ترتيب عناصر المصفوفة - الوصول إلى عنصر Console.WriteLine("intArray @ 0: " + intArray[0]); المصفوفات قابلة للتعديل. intArray[1] = 1; القوائم تُستخدَم القوائم أكثر من المصفوفات لما توفّره من مرونة وصيغة تعريف قائمة هي على النحو التالي: ;List<datatype> <var name> = new List<datatype>() List<int> intList = new List<int>(); List<string> stringList = new List<string>(); List<int> z = new List<int> { 9000, 1000, 1337 }; // تحديد القيم الابتدائية لعناصر القائمة تُستخدَم الإشارتان <> للأنواع العميمة Generics - راجع فقرة ميزات رائعة ليست للقوائم قيم مبدئية ويجب أولا إضافة قيمة قبل إمكانية الوصول إلى العنصر intList.Add(1); Console.WriteLine("intList @ 0: " + intList[0]); بنى تحتية أخرى يجدر بك مراجعتها: قوائم الانتظار Queues/ الرصوص Stacks القواميس Dictionaries HashSet تجميعاـ القراءة فقط Read-only collections الأزواج المُرتّبة Tuples (الإصدار 4 من .NET. فما فوق) العوامل Console.WriteLine("\n→Operators"); int i1 = 1, i2 = 2; // اختصار لتعريف متغيّرات عدة في آن واحد العمليات الحسابية واضحة Console.WriteLine(i1 + i2 - i1 * 3 / 7); // => 3 المقياس Modulo Console.WriteLine("11%3 = " + (11 % 3)); // => 2 عوامل المقارنة Console.WriteLine("3 == 2? " + (3 == 2)); // => false Console.WriteLine("3 != 2? " + (3 != 2)); // => true Console.WriteLine("3 > 2? " + (3 > 2)); // => true Console.WriteLine("3 < 2? " + (3 < 2)); // => false Console.WriteLine("2 <= 2? " + (2 <= 2)); // => true Console.WriteLine("2 >= 2? " + (2 >= 2)); // => true عوامل المقارنة البتّية Bitwise ~ عامل التكملة الأحادي (إن كان البت يحوي 0 يحوله إلى 1، وإن كان يحوي واحد يحوّله إلى صفر) >> إزاحة البتات إلى اليسار << إزاحة البتات إلى اليمين & عامل “و” المنطقي ^ عامل “أو” المنطقي غير الشامل exclusive OR | عامل “أو” المنطقي الشامل inclusive OR التزايد Incrementation int i = 0; Console.WriteLine("\n->Inc/Dec-rementation"); Console.WriteLine(i++); //Prints "0", i = 1. تزاد بعدي Console.WriteLine(++i); //Prints "2", i = 2. تزايد قبلي Console.WriteLine(i--); //Prints "2", i = 1. تناقص بعدي Console.WriteLine(--i); //Prints "0", i = 0. تناقص قبلي بنى التحكّم Console.WriteLine("\n->Control Structures"); تتبع بنية التحكم if else طريقة كتابة بنى التحكم في C int j = 10; if (j == 10) { Console.WriteLine("I get printed"); } else if (j > 10) { Console.WriteLine("I don't"); } else { Console.WriteLine("I also don't"); } العوامل الثلاثية بنية تحكّم if else بسيطة تمكن كتابتها على النحو التالي: <condition> ? <true> : <false> int toCompare = 17; string isTrue = toCompare == 17 ? "True" : "False"; حلقة While التكرارية int fooWhile = 0; while (fooWhile < 100) { //تتكرّر الحلقة مئة مرة، من القيمة 0 إلى القيمة 99 fooWhile++; } حلقة Do.. While التكرارية int fooDoWhile = 0; do { الحلقة معدّة للتكرار مئة مرة، من القيمة 0 إلى القيمة 99 Start iteration 100 times, fooDoWhile 0->99 if (false) continue; // تجاوز التكريرة الحالية fooDoWhile++; if (fooDoWhile == 50) break; // توقيف الحلقة تماما، والخروج منها } while (fooDoWhile < 100); حلقة for التكرارية ذات الصيغة: (<for(<start_statement>; <conditional>; <step for (int fooFor = 0; fooFor < 10; fooFor++) { // تتكرّر الحلقة عشر مرات، من القيمة 0 إلى القيمة 9 } حلقة For Each يمكن استخدام حلقة التكرار foreach للمرور عبر أي كائن Object يُنفّذ الصنف IEnumerable أو <IEnumerable<T تنفّذ جميع الأنواع التجميعية (المصفوفات، القوائم، القواميس…) في إطار العمل .Net واجهة أو أكثر من الأصناف المذكورة (يمكن حذف ()ToCharArray من التعليمة أدناه، لأن String تنفّذ الواجهة IEnumerable) foreach (char character in "Hello World".ToCharArray()) { // تمرّ على جميع المحارف في السلسلة } تعليمة Switch تعمل Switch مع أنواع البيانات byte, short, char, وint تعمل كذلك مع أنواع البيانات Enum (نتعرّض لها أدناه)، الصنف String وبضعة أصناف خاصّة تغلّف أنواع بيانات أساسية: Character,Byte,Short, و Integer. int month = 3; string monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; يمكن تنفيذ أكثر من إجراء في كل حالة case، إلا أنه لا يمكن إضافة إجراء ضمن حالة دون إضافة تعليمة توقيف break; قبل الحالة الموالية (إن أردت فعل هذا الأمر، فستحتاج لإضافة تعليمة goto case x بعد الإجراء) case 6: case 7: case 8: monthString = "Summer time!!"; break; default: monthString = "Some other month"; break; } التحويل بين أنواع البيانات وجبْر الأنواع Typecasting تحويل البيانات تحويل سلسلة محارف String إلى عدد Integer سيظهر استثناء Exception في حالة إخفاق عملية التحويل int.Parse("123");// نحصُل على النسخة العددية من سلسلة المحارف "123" عند استخدام الدالة TryParse لتحويل نوع البيانات فإن قيمة التحويل ستكون القيمة المبدئية لنوع البيانات وفي حالة الأعداد فإن القيمة المبدئية هي 0 int tryInt; if (int.TryParse("123", out tryInt)) // ترجع الدالة قيمة منطقية Console.WriteLine(tryInt); // 123 تحويل الأعداد إلى سلاسل محارف String يتضمّن الصنف Convert عددا من التوابع Methods لتسهيل التحويل Convert.ToString(123); أو tryInt.ToString(); جبر أنواع البيانات جبر العدد العشري 15 للحصول على قيمة من النوع int ثم جبر القيمة المُتحصَّل عليها ضمنيا لنحصُل على النوع long long x = (int) 15M; } الأصناف راجع التعريفات في آخر الملف public static void Classes() { انظر تعريف الكائنات في آخر الملف استخدم الكلمة المفتاحية new لاستهلال صنف Bicycle trek = new Bicycle(); استدعاء توابع الكائن trek.SpeedUp(3); // يجب دائما المرور عبر المعدّلات والمسترجعات Setter and getter methods trek.Cadence = 100; يُستخدم التابع ToString لعرض قيمة الكائن Console.WriteLine("trek info: " + trek.ToString()); استهلال كائن جديد من الصنف PennyFarthing PennyFarthing funbike = new PennyFarthing(1, 10); Console.WriteLine("funbike info: " + funbike.ToString()); Console.Read(); } // نهاية التابع الرئيس Main method مَدخل الكونسول Console entry. التطبيقات التي تعمل عبر الطرفية يجب أن يكون لديها مدخل عبارة عن تابع رئيس public static void Main(string[] args) { OtherInterestingFeatures(); } ميزات مثيرة للاهتمام التوقيعات المبدئية للتوابع public // مجال الرؤية static // يسمح بالاستدعاء المباشر من الصنف دون المرور بكائنات int // نوع البيانات المُرجَعة, MethodSignatures( int maxCount, // المتغيّر الأول عددي int count = 0, // القيمة المبدئية هي 0، تُستخدَم إن لم يُمرَّر متغير إلى التابع int another = 3, params string[] otherParams // يستقبل بقية المتغيّرات المُمررة إلى التابع جميعا ) { return -1; } يمكن أن تكون أسماء التوابع متطابقة، ما دامت التوقيعات مختلفة وكل تابع لا يختلف عن آخر سوى في نوع البيانات المُرجَع ليس وحيدا public static void MethodSignatures( ref int maxCount, // تمرير المعاملات حسب المرجع، وليس القيمة out int count) { المعامل المُمرر في المتغيّر count سيحوي القيمة 15 خارج هذه الدالة count = 15; // معامل الخروج out يجب أن يُسنَد قبل الانتهاء من التابع } أنواع البيانات العميمة Generics الأصناف TKey وTValue يحدّدها المستخدم الذي يستدعي هذه الدالة ويحاكي هذا التابع عمل SetDefault في بايثون public static TValue SetDefault<TKey, TValue>( IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultItem) { TValue result; if (!dictionary.TryGetValue(key, out result)) return dictionary[key] = defaultItem; return result; } يمكنك تقييد الكائنات التي يمكن تمريرها إلى الدالة public static void IterateAndPrint<T>(T toPrint) where T: IEnumerable<int> { بما أن الصنف T ينفّذ IEnumerable فإنه يمكننا المرور على عناصره باستخدام foreach foreach (var item in toPrint) // العنصر هو من النوع int Console.WriteLine(item.ToString()); } الكلمة المفتاحية “yield” يدلّ استخدام yield أن التابع الذي تظهر فيه هذه الكلمة المفتاحية لديه خاصيّة التكرار (أي أنه يمكن استخدام التابع مع الحلقة foreach) public static IEnumerable<int> YieldCounter(int limit = 10) { for (var i = 0; i < limit; i++) yield return i; } نستطيع استدعاء التابع أعلاه على النحو التالي public static void PrintYieldCounterToConsole() { foreach (var counter in YieldCounter()) Console.WriteLine(counter); } يمكن استخدام yield return أكثر من مرّة في نفس التابع public static IEnumerable<int> ManyYieldCounter() { yield return 0; yield return 1; yield return 2; yield return 3; } كما يمكنك استخدام “yield break” لتوقيف التكرار التابع التالي يُرجِع نصف القيم الموجودة بين 0 وlimit public static IEnumerable<int> YieldCounterWithBreak(int limit = 10) { for (var i = 0; i < limit; i++) { if (i > limit/2) yield break; yield return i; } } public static void OtherInterestingFeatures() { المعاملات الاختيارية MethodSignatures(3, 1, 3, "Some", "Extra", "Strings"); MethodSignatures(3, another: 3); // تعيين قيمة المعامل مباشرة، مع تجاوز المعاملات الاختيارية تمرير المعاملات بالمرجع By reference، والقيمة المُرجعة Out parameter BY REF AND OUT PARAMETERS int maxCount = 0, count; // المعاملات المُمررة بالمرجع يجب أن تحوي قيمة MethodSignatures(ref maxCount, out count); توابع التمديد Extension methods int i = 3; i.Print(); // مُعرَّفة أدناه الأنواع التي تقبل قيمة فارغة Nullable types، مناسبة للتخاطب مع قواعد البيانات والقيم المُرجَعة وأي نوع بيانات قيمي (أي ليس صنفا) يمكن جعله يقبل قيما فارغة بكتابة ? بعده <type>? <var name> = <value> int? nullable = null; // اختصار لـ Nullable<int> Console.WriteLine("Nullable variable: " + nullable); bool hasValue = nullable.HasValue; // قيمة منطقية صحيحة true إن لم يكن يساوي null علامتا الاستفهام المتلاصقتان ?? هما اختصار لتحدد قيمة مبدئية في حال كان المتغيّر فارغا نعطيه 0 قيمة مبدئية int notNullable = nullable ?? 0; // 0 ?. هذه العلامة عي عامل للتحقّق من القيمة الفارغة null nullable?.Print(); // استخدم تابع التمديد Print() إذا كان المتغيّر nullable مختلفا عن null المتغيّرات ضمنية النوع - يمكنك ترك المُصرّف Compiler يحدّد نوع المتغيّر: var magic = "magic is a string, at compile time, so you still get type safety"; ;magic = 9 لن تُسنَد القيمة 9 إلى المتغيّر magic لأنه يحوي سلسلة محارف الأنواع العميمة var phonebook = new Dictionary<string, string>() { {"Sarah", "212 555 5555"} // إضافة عنوان إلى دفتر العناوين }; استدعاء الدالة SetDefault المُعرَّفة أعلاه Console.WriteLine(SetDefault<string,string>(phonebook, "Shaun", "No Phone")); // No Phone يمكنك عدم تحديد TKey و TValue بما أنه يمكن استنتاجهما تلقائيا Console.WriteLine(SetDefault(phonebook, "Sarah", "No Phone")); // 212 555 5555 الدوال مجهولة الاسم Lambda expressions - تتيح كتابة شفرات على نفس السطر Func<int, int> square = (x) => x * x; // آخر عنصر من T هو القيمة المُرجعة Console.WriteLine(square(3)); // 9 التعامل مع الأخطاء try { var funBike = PennyFarthing.CreateWithGears(6); لن تُنفَّذ لأن CreateWithGears تتسبّب في استثناء Exception string some = ""; if (true) some = null; some.ToLower(); // تتسبّب في الاستثناء NullReferenceException } catch (NotSupportedException) { Console.WriteLine("Not so much fun now!"); } catch (Exception ex) // التقاط جميع الاستثناءات الأخرى { throw new ApplicationException("It hit the fan", ex); // throw; // التقاط آخر يحافظ على ركام النداء callstack } // catch { } // التقاط كل شيء دون التعامل مع الاستثناءات finally { // try أو catch تُنفّذ بعد } إدارة الموارد يمكنك إدارة الموارد المتوفّرة بسهولة حيث تنفّذ أغلب الكائنات التي تستعمل الموارد غير المستغلة (الملفات، سياق الأجهزة الطرفية، …إلخ) تُنفّذ الواجهة IDisposable تتولّى التعليمة using التخلّص من كائنات IDisposable using (StreamWriter writer = new StreamWriter("log.txt")) { writer.WriteLine("Nothing suspicious here"); } يُتخلَّص من جميع الموارد بعد الانتهاء من تنفيذ هذه الشفرة حتى ولو تسبّبت في استثناء البرمجة المتوازية var words = new List<string> {"dog", "cat", "horse", "pony"}; Parallel.ForEach(words, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, word => { Console.WriteLine(word); } ); تشغيل هذه الشفرة سينتُج عنه مُخرجات متعدّدة لأن كلّ تشعّب thread يُكمل في وقت مختلف عن الآخر. أدناه أمثلة على المُخرجات cat dog horse pony dog horse pony cat الكائنات الديناميكية (رائعة للعمل مع لغات برمجة أخرى) dynamic student = new ExpandoObject(); student.FirstName = "First Name"; لا تحتاج لتعريف صنف أولا بل إنه يمكنك إضافة توابع (يُرجع التابع أدناه سلسلة محارف ويتلقّى سلسلة محارف) student.Introduce = new Func<string, string>( (introduceTo) => string.Format("Hey {0}, this is {1}", student.FirstName, introduceTo)); Console.WriteLine(student.Introduce("Beth")); تنفّذ أغلب التجميعات Collections الواجهة <IQUERYABLE<T التي توفّر الكثير من التوابع المفيدة var bikes = new List<Bicycle>(); // دراجات هوائية bikes.Sort(); // يرتّب القائمة bikes.Sort((b1, b2) => b1.Wheels.CompareTo(b2.Wheels)); // يرتّب القائمة بناءً على عدد العجلات var result = bikes .Where(b => b.Wheels > 3) // الترشيج والفلترة .Where(b => b.IsBroken && b.HasTassles) .Select(b => b.ToString()); // var sum = bikes.Sum(b => b.Wheels); يجمع عدد العجلات في كامل القائمة وينشئ قائمة من الكائنات الضمنية Implicit objects بالاعتماد على بعض خواص الدراجة الهوائية var bikeSummaries = bikes.Select(b=>new { Name = b.Name, IsAwesome = !b.IsBroken && b.HasTassles }); من الصعب توضيح الأمر هنا، إلا أنك تحصُل على نوع البيانات قبل الانتهاء من التعليمات، إذ أن المصرّف يمكنه العمل ضمنا على الأنواع أعلاه foreach (var bikeSummary in bikeSummaries.Where(b => b.IsAwesome)) Console.WriteLine(bikeSummary.Name); التوازي مع ASPARALLEL نخلط عمليّات LINQ والعمليّات المتوازية var threeWheelers = bikes.AsParallel().Where(b => b.Wheels == 3).Select(b => b.Name); سيحدث الأمر بالتوازي. تُنشَأ التشعبات تلقائيا وستُقسَّم النتائج بينها طريقة رائعة للعمل مع مجموعة بيانات ضخمة إن كانت لديك الكثير من الأنوية Cores LINQ تربط بين مخزن بيانات وكائنات من الصنف <IQueryable<T مثلا: LinqToSql تربط الكائنات مع قاعدة بيانات، LinqToXml تربط الكائنات مع مستند XML var db = new BikeRepository(); يؤجَّل التنفيذ، وهو أمر جيّد عند التعامل مع قواعد البيانات var filter = db.Bikes.Where(b => b.HasTassles); // no query run if (42 > 6) // يمكنك الاستمرار في إضافة المرشحات، حتى تلك المشروطة؛ مناسبة لميزة "البحث المتقدّم" filter = filter.Where(b => b.IsBroken); // no query run var query = filter .OrderBy(b => b.Wheels) .ThenBy(b => b.Name) .Select(b => b.Name); // still no query run يعمل الاستعلام الآن، إلا أنك لا تحصُل على نتائج الاستعلام إلا عند المرور عليها foreach (string bike in query) Console.WriteLine(result); } } // نهاية الصنف LearnCSharp يمكنك إضافة أصناف أخرى في ملف cs. public static class Extensions { توابع الصنف Extensions public static void Print(this object obj) { Console.WriteLine(obj.ToString()); } } التفويض والأحداث public class DelegateTest { public static int count = 0; public static int Increment() { // زيادة العدّاد ثم إرجاع النتيجة return ++count; } التفويض delegate هو مرجع لتابع لجعل مرجع على التابع Increment نبدأ بتعريف تفويض بنفس التوقيع أي أنه لا يأخذ أية معطيات ويُرجع عددا من النوع int public delegate int IncrementDelegate(); يمكن أيضا استخدام حدث Event لتحريك التفويض أنشئ حدثا بنوع التفويض public static event IncrementDelegate MyEvent; static void Main(string[] args) { نحيل إلى التابع Increment باستهلال التفويض وتمرير معطى هو التابع نفسه IncrementDelegate inc = new IncrementDelegate(Increment); Console.WriteLine(inc()); // => 1 يمكن تركيب التفويضات بالعامل + IncrementDelegate composedInc = inc; composedInc += inc; composedInc += inc; سينفّذ التفويض composedInc التابع Increment ثلاث مرات Console.WriteLine(composedInc()); // => 4 الاشتراك في الحدث باستخدام التفويض MyEvent += new IncrementDelegate(Increment); MyEvent += new IncrementDelegate(Increment); تحريك الحدث، أي تنفيذ كل التفويضات المشترِكة في هذا الحدث Console.WriteLine(MyEvent()); // => 6 } } صيغة تعريف صنف: <public/private/protected/internal> class <class name>{` // حقول البيانات، المشيّدات، الدوالّ.. كلّها في الداخل //تُستدعى الدوال بنفس طريقة استدعاء التوابع في جافا } public class Bicycle { // حقول/متغيّرات صنف الدراجات الهوائية Bicycle public int Cadence // عمومي public : يمكن استدعاء من أي مكان { get // مسترجع - نعرّف تابعا للوصول إلى قيمة خاصيّة من الكائن { return _cadence; } set // معدّل - نعرّف تابعا لتعيين قيمة خاصيّة { _cadence = value; // القيمة value هي المعطى المُمرّر إلى المعدّل } } private int _cadence; protected virtual int Gear // يمكن استدعاءه فقط من هذا الصنف أو الأصناف المتفرّعة منه Protected:محميّ { get; // تُنشأ خاصيّة تلقائية بحيث لا تحتاج لإضافة حقل بيانات set; } internal int Wheels // داخليّ Internal: يُمكن الوصول إليه من نفس الملف التنفيذي { get; private set; // يمكن تغيير مجال المسترجعات والمعدّلات } int _speed; // ولا يمكن الوصول إليها إلا من داخل الصنف Private كل الخاصيّات هي مبدئيا خاصّة // يمكن أيضا استخدام الكلمة المفتاحية private public string Name { get; set; } للخاصيّات صياغة استثنائية عندما نريد خاصية متاحة للقراءة فقط بمعنى أنها تعيد فقط نتيجة عبارة public string LongName => Name + " " + _speed + " speed"; النوع enum هو نوع بيانات قيمية يتمثّل في مجموعة من المتغيّرات ثابتة القيمة هذا النوع هو في الواقع مجرّد ربط اسم بقيمة (عددية، إن لم يحدد نوع آخر) أنواع البيانات الموثوقة في قيم الثوابت هي byte, sbyte, short, ushort, int, uint, long, و ulong ولا يمكن أن توجد نفس القيمة مرتين في متغيّر من النوع enum public enum BikeBrand { AIST, BMC, Electra = 42, // مباشرة enum يمكن تعيين قيمة المتغيّر في Gitane // 43 } عرّفنا هذا النوع داخل الصنف Bicycle لذا فهو نوع داخلي وعندما نريد استخدامه خارج الصنف فسيتوجّب أن نكتُب Bicycle.Brand public BikeBrand Brand; بعد تعريف نوع enum يصبح بإمكاننا تعريف متغيّر من هذا النوع تستطيع التعليم على وجود قيم عدّة يمكن الاختيار بينها بإضافة الصنف FlagsAttribute قبل تعريف النوع enum يمكن استخدام أي صنف متفرّع عن الصنف Attribute لتعليم أنواع البيانات، التوابع والمعاملات…إلخ يمكن استخدام العوامل المنطقية & و | لإجراء عمليّات منطقية داخل القيمة [Flags] public enum BikeAccessories { None = 0, Bell = 1, MudGuards = 2, // نحتاج لتعيين القيم يدويا Racks = 4, Lights = 8, FullPackage = Bell | MudGuards | Racks | Lights } الاستخدام: aBike.Accessories.HasFlag(Bicycle.BikeAccessories.Bell) في الإصدارات السابقة على الإصدار الرابع من إطار العمل NET (aBike.Accessories & Bicycle.BikeAccessories.Bell) == Bicycle.BikeAccessories.Bell public BikeAccessories Accessories { get; set; } تنتمي الخاصيّات المُعلمة بالكلمة المفتاحية static للصنف نفسه، وليس لكائن عكس بقية الخاصيّات يمكن الوصول إلى هذه الخاصيّات دون الرجوع إلى كائن محدّد ;Console.WriteLine("Bicycles created: " + Bicycle.bicyclesCreated) public static int BicyclesCreated { get; set; } عيّن القيم غير القابلة للتعديل أثناء التشغيل ولا يمكن إسناده إلا عند تعريفها أو داخل مشيّد readonly bool _hasCardsInSpokes = false; // خاصيّة خاصّة وللقراءة فقط المشيّدات Constructors هي طريقة لإنشاء الأصناف أدناه المشيّد المبدئي public Bicycle() { this.Gear = 1; // يمكن الوصول إلى خاصيّات الصنف بالكلمة المفتاحية this Cadence = 50; // إلا أنك لا تحتاجها في كل الحالات _speed = 5; Name = "Bontrager"; Brand = BikeBrand.AIST; BicyclesCreated++; } هذا مشيّد مُعيّن (يحوي معطيات) public Bicycle(int startCadence, int startSpeed, int startGear, string name, bool hasCardsInSpokes, BikeBrand brand) : base() // أولا base يستدعي { Gear = startGear; Cadence = startCadence; _speed = startSpeed; Name = name; _hasCardsInSpokes = hasCardsInSpokes; Brand = brand; } يمكن وضع المشيّدات بالتسلسل public Bicycle(int startCadence, int startSpeed, BikeBrand brand) : this(startCadence, startSpeed, 0, "big wheels", true, brand) { } صيغة كتابة الدوال (<public/private/protected> <return type> <function name> <args>) يمكن للأصناف أن تعيّن مسترجعات ومعدّلات لحقولها كما يمكنها تنفيذ الخاصيّات عبر دوال (وهي الطريقة المفضّلة في Csharp) ويمكن لمعاملات التابع أن تحوي قيما مبدئية، في هذه الحالة، يمكن استدعاء التوابع بدون تمرير معطيات عن هذه العوامل public void SpeedUp(int increment = 1) { _speed += increment; } public void SlowDown(int decrement = 1) { _speed -= decrement; } تعيّن الخاصيّات القيم وتسترجعها. إن كان غرضك الوصول إلى البيانات فقط دون تعديلها فالخاصيّات أنسب. يمكن أن يكون للخاصيّة مسترجع أو معدّل أو كلاهما private bool _hasTassles; // متغيّر خاص public bool HasTassles // مسترجع عام { get { return _hasTassles; } set { _hasTassles = value; } } كما يمكنك تعريف خاصيّة تلقائية في سطر واحد ستُنشئ هذه الصيغة حقلا داعما تلقائيا يمكنك الحد من مجال الرؤية على المسترجع أو المعدّل أو كليهما public bool IsBroken { get; private set; } يمكن للخاصيّات أن تكون تلقائية التنفيذ public int FrameSize { get; // يمكنك الحد من مجال الرؤية على المسترجع أو المعدّل //Framesize يمكنه استدعاء معدّل Bicycle يعني هذا أن الصنف private set; } يمكن تعريف فهرس على الكائنات يمكنك مثلا كتابة bicycle[0] التي ترجع القيمة “chris” للحصول على أول راكب أو كتابة “bicycle[1] = "lisa لتعيين الراكب الثاني (دراجة رباعية المقاعد!) private string[] passengers = { "chris", "phil", "darren", "regina" }; public string this[int i] { get { return passengers[i]; } set { passengers[i] = value; } } تابع لعرض قيم حقول الكائن public virtual string Info() { return "Gear: " + Gear + " Cadence: " + Cadence + " Speed: " + _speed + " Name: " + Name + " Cards in Spokes: " + (_hasCardsInSpokes ? "yes" : "no") + "\n------------------------------\n" ; } يمكن للتوابع أن تكون ثابتة (الكلمة المفتاحية static). مناسبة للدوال المساعدة public static bool DidWeCreateEnoughBicycles() { داخل التابع الثابت لا يمكن إجراء عمليات سوى على الحقول الثابتة return BicyclesCreated > 9000; } إن كان الصنف لا يحتاج إلا إلى حقول ثابتة فربما يكون من الأفضل أن يكون الصنف نفسه ثابتا } // نهاية الصنف Bicycle PennyFarthing هو صنف متفرّع من الصنف Bicycle class PennyFarthing : Bicycle { (يمثّل هذا الصنف تلك الدراجات الهوائية التي لديها عجلة أمامية كبيرة جدا، وليست لديها مسنّنات Gears لتعديل السرعة) . نستدعي مشيّد الصنف الأب public PennyFarthing(int startCadence, int startSpeed) : base(startCadence, startSpeed, 0, "PennyFarthing", true, BikeBrand.Electra) { } protected override int Gear { get { return 0; } set { throw new InvalidOperationException("You can't change gears on a PennyFarthing"); } } public static PennyFarthing CreateWithGears(int gears) { var penny = new PennyFarthing(1, 1); // عمليا لا توجد دراجة من نوع PennyFarthing بمسنّنات penny.Gear = gears; return penny; } public override string Info() { string result = "PennyFarthing bicycle "; result += base.ToString(); // نستدعي التابع الأصلي الموجود في الصنف الأب return result; } } تحتوي الواجهات على التوقيعات فقط interface IJumpable { void Jump(int meters); // جميع الأعضاء في الواجهة هي مبدئيا عمومية } interface IBreakable { bool Broken { get; } // يمكن للواجهات أن تحوي خاصيّات كما يمكنها أن تتضمّن واجهات وأحداثا } يمكن للأصناف أن ترث Inherit من صنف واحد آخر على الأكثر، إلا أنه يمكنها تنفيذ أي عدد من الواجهات ويجب أن يكون الصنف الأب الأول في لائحة الأصناف تليه الواجهات كلّها class MountainBike : Bicycle, IJumpable, IBreakable { int damage = 0; public void Jump(int meters) { damage += meters; } public bool Broken { get { return damage > 100; } } } صنف للاتصال بقاعدة البيانات، نستخدمه مثالا لعمل LinqToSql يعمل إطار العمل EntityFramework Code First لربط الكائنات بسجلات جداول البيانات (بنفس طريقة ActiveRecord في روبي، إلا أنه ثنائي الاتجاه) public class BikeRepository : DbContext { public BikeRepository() : base() { } public DbSet<Bicycle> Bikes { get; set; } } يمكن تقسيم الأصناف على ملفات cs. عدّة A1.cs public partial class A { public static void A1() { Console.WriteLine("Method A1 in class A"); } } A2.cs public partial class A { public static void A2() { Console.WriteLine("Method A2 in class A"); } } يستخدم الصنف Program أدناه الصنف A المُقسّم على ملفي cs. Program using the partial class "A" public class Program { static void Main() { A.A1(); A.A2(); } } يمكن الإداراج في سلاسل المحارف String interpolation بكتابة $ أمام السلسلة ثم إحاطة المتغيّر المُدرج بقوسين معكوفين { }. يمكنك أيضا تجميع السلسلتين، الأصلية والمُعدّلة، بالعلامة @ public class Rectangle { public int Length { get; set; } public int Width { get; set; } } class Program { static void Main(string[] args) { Rectangle rect = new Rectangle { Length = 5, Width = 3 }; Console.WriteLine($"The length is {rect.Length} and the width is {rect.Width}"); string username = "User"; Console.WriteLine([email protected]"C:\Users\{username}\Desktop"); } } ميزات جديدة في الإصدار C# 6 class GlassBall : IJumpable, IBreakable { تمهيد الخاصيّات التلقائية public int Damage { get; private set; } = 0; تمهيد الخاصيّات التلقائية المقتصرة على المسترجعات public string Name { get; } = "Glass ball"; تمهيد الخاصيّات التلقائية المقتصرة على المسترجعات في المشيّد public string GenieName { get; } public GlassBall(string genieName = null) { GenieName = genieName; } public void Jump(int meters) { if (meters < 0) العبارة nameof() مستحدثة وينتُج عنها التحقّق من وجود المعرّف "nameof(x) == "x تحول على سبيل المثال دون بقاء أسماء المتغيّرات القديمة في رسائل الخطأ بعد تحديثها throw new ArgumentException("Cannot jump negative amount!", nameof(meters)); Damage += meters; } الخاصيّات المعرَّفة ضمن هيكل العبارة public bool Broken => Damage > 100; نفس الشيء بالنسبة للتوابع public override string ToString() // سلسلة محارف تُدرج ضمنها متغيّرات => $"{Name}. Damage taken: {Damage}"; public string SummonGenie() العوامل المشترطة بالقيمة الفارغة null ترجع العبارة x?.y القيمة null بمجرد كون x مساوية ل null، بدون تقييم y => GenieName?.ToUpper(); } static class MagicService { private static bool LogException(Exception ex) { /* سجّل الاستثناءات في مكان ما */ log exception somewhere */ return false; } public static bool CastSpell(string spell) { try { // API نفترض هنا أننا نستدعي واجهة تطبيقات برمجية throw new MagicServiceException("Spell failed", 42); // نجح الاستدعاء return true; } يلتقط استثناء في حالة إخفاق استدعاء واجهة التطبيقات، أي أن قيمة Code تساوي 42 Only catch if Code is 42 i.e. spell failed catch(MagicServiceException ex) when (ex.Code == 42) { // أخفق الاستدعاء return false; } استثماءات أخرى أو الاستثناء MagicServiceException عندما تكون قيمة المتغير Code مختلفة عن 42 catch(Exception ex) when (LogException(ex)) { // لا يصل التنفيذ إلى هذه الكتلة } return false; } لاحظ أن التقاط الاستثناء MagicServiceException وإعادة إطلاقه عندما يكون المتغير Code لا يساوي القيمة 42 أو 117 هو أمر مختلف، إذ أن كتلة catch-all الأخيرة لن تلتقط الاستثناء المُعاد public class MagicServiceException : Exception { public int Code { get; } public MagicServiceException(string message, int code) : base(message) { Code = code; } } الخاصية Obsolete public static class PragmaWarning { [Obsolete("Use NewMethod instead", false)] public static void ObsoleteMethod() { /*شفرة برمجية قديمة هنا */ } public static void NewMethod() { /* شفرة برمجية جديدة */ } public static void Main() { ObsoleteMethod(); تحذير يظهر عند استخدام شفرة برمجية قديمة، ناتج عن الوسم Obsolete أعلاه CS0618: 'ObsoleteMethod is obsolete: Use NewMethod instead' تعطّل التعليمة التالية إظهار التحذير السابق #pragma warning disable CS0618 ObsoleteMethod(); // لا تحذير #pragma warning restore CS0618 ObsoleteMethod(); // CS0618: 'ObsoleteMethod is obsolete: Use NewMethod instead' } } } // نهاية فضاء الأسماء using System; ميزة في C# 6: إمكانية استخدام static مع using using static System.Math; namespace Learning.More.CSharp { class StaticUsing { static void Main() { // using مع static بدون استخدام Console.WriteLine("The square root of 4 is {}.", Math.Sqrt(4)); // using مع static باستخدام Console.WriteLine("The square root of 4 is {}.", Sqrt(4)); } } } ميزة جديدة في C# 7 ثبّت آخر إصدار من Microsoft.Net.Compilers باستخدام Nuget ثبّت آخر إصدار من System.ValueTuple باستخدام Nuget using System; namespace Csharp7 { الأزواج المرتبة Tuples، التفكيك DECONSTRUCTION والإلغاءات Discards class TuplesTest { public (string, string) GetName() { // Item1، Item2 .... تُسمى الحقول في الأزواج المرتبة مبدئيا بـ var names1 = ("Peter", "Parker"); Console.WriteLine(names1.Item2); // => Parker يمكن تخصيص أسماء الحقول تعريف النوع الأول (string FirstName, string LastName) names2 = ("Peter", "Parker"); تعريف النوع الثاني var names3 = (First:"Peter", Last:"Parker"); Console.WriteLine(names2.FirstName); // => Peter Console.WriteLine(names3.Last); // => Parker return names3; } public string GetLastName() { var fullName = GetName(); يمكن تفكيك الأزواج المرتبة (string firstName, string lastName) = fullName; يمكن إلغاء حقول من الزوج المرتب بعد تفكيكه بالعلامة _ Fields in a deconstructed tuple can be discarded by using _ var (_, last) = fullName; return last; } يمكن تفكيك أي نوع بيانات على نفس المنوال باستخدام التابع Deconstruct public int randomNumber = 4; public int anotherRandomNumber = 10; public void Deconstruct(out int randomNumber, out int anotherRandomNumber) { randomNumber = this.randomNumber; anotherRandomNumber = this.anotherRandomNumber; } static void Main(string[] args) { var tt = new TuplesTest(); (int num1, int num2) = tt; Console.WriteLine($"num1: {num1}, num2: {num2}"); // => num1: 4, num2: 10 Console.WriteLine(tt.GetLastName()); } } مطابقة الأنماط Pattern matching class PatternMatchingTest { public static (string, int)? CreateLogMessage(object data) { switch(data) { // when ترشيح إضافي باستخدام case System.Net.Http.HttpRequestException h when h.Message.Contains("404"): return (h.Message, 404); case System.Net.Http.HttpRequestException h when h.Message.Contains("400"): return (h.Message, 400); case Exception e: return (e.Message, 500); case string s: return (s, s.Contains("Error") ? 500 : 200); case null: return null; default: return (data.ToString(), 500); } } } الإحالة إلى الموارد المحلية Reference locals تعطيك إمكانية إرجاع مرجع Reference كائن بدلا من قيمته class RefLocalsTest { لاحظ الكلمة المفتاحية ref في تعليمة الإرجاع return public static ref string FindItem(string[] arr, string el) { for(int i=0; i<arr.Length; i++) { if(arr[i] == el) { // إرجاع المرجع return ref arr[i]; } } throw new Exception("Item not found"); } public static void SomeMethod() { string[] arr = {"this", "is", "an", "array"}; //في كل مكان ref لاحظ ref string item = ref FindItem(arr, "array"); item = "apple"; Console.WriteLine(arr[3]); // => apple } } الدوال المحليّة Local functions class LocalFunctionTest { private static int _id = 0; public int id; public LocalFunctionTest() { id = generateId(); لا يمكن الوصول إلى الدالة المحلية خارج هذا المجال int generateId() { return _id++; } } public static void AnotherMethod() { var lf1 = new LocalFunctionTest(); var lf2 = new LocalFunctionTest(); Console.WriteLine($"{lf1.id}, {lf2.id}"); // => 0, 1 int id = generateId(); // خطأ // error CS0103: The name 'generateId' does not exist in the current context } } } ترجمة -وبتصرّف- للمقال Learn C# in Y Minutes
  17. فلنفترض أنك تريد تشغيل دالة اسمها bounceBall أربع مرات. كيف يمكنك ذلك؟ هل ستختار الطريقة التالية؟: function bounceBall() { // bounce the ball here } bounceBall() bounceBall() bounceBall() bounceBall() يمكن أن تكون الطريقة أعلاه مناسبة إذا كنت تريد تنفيذ الدالة بضع مرات. مالذي سيحدُث إن احتجت لتنفيذ الدالة bounceBall مئة مرة؟ الطريقة الأفضل في هذه الحالة هي استخدام تعليمة التكرار الحلقي for. تعليمة التكرار الحلقي for تنفّذ تعليمة التكرار الحلقي for كتلة من التعليمات البرمجية بعدد المرات التي تريد. في ما يلي مثال على تنفيذ الدالة bounceBall عشر مرات: for (let i = 0; i < 10; i++) { bounceBall() } تتكوّن التعليمة for من أربعة أجزاء: العبارة الابتدائية initialExpression الشرط condition عبارة الزيادة incrementalExpression والجملة statement for (initialExpression; condition; incrementExpression) { statement } ستحتاج عندما تريد تنفيذ تعليمة تكرار حلقي إلى جملة برمجية Statement. الجملة هي كتلة الشفرة البرمجية التي تريد تنفيذها مرات عدّة. يمكنك كتابة أي شفرة ترغب بها مهما كان عدد أسطرها، كما يمكنك استخدام دوال. تأخذ التعليمة for الصيغة التالية عند استخدام الدالة bounceBall في الجزء الخاصّ بالجملة: for (initialExpression; condition; incrementExpression) { bounceBall() } ستحتاج كذلك إلى تعريف عبارة ابتدائية Initial expression تبدأ بها تعليمة التكرار الحلقي. هذا هو الجزء الذي تعرّف فيه متغيرا. من المتعارف عليه تسمية المتغير الابتدائي في تعليمة for بالحرف i وإعطاؤه القيمة 0. هكذا تبدو صيغة for عند تحديد عبارتها الابتدائية: for (let i = 0; condition; incrementExpression) { bounceBall() } نزيد قيمة المتغيّر (i في مثالنا) - أو ننقُصه - بعد تنفيذ الحلقة التكرارية لأول مرة، وذلك عن طريق عبارة الزيادة Incremental expression. لزيادة قيمة المتغير i بواحد نعيد إسناده بحيث يُصبح يساوي i + 1 أي بالعبارة i = i + 1. تُختَصر العبارة i = i + 1على ++i والتي يكثُر استخدامها في حلقات التكرار. لنقص قيمة المتغير i بواحد نعيد إسناده بحيث يُصبح يساوي i - 1، أي بالعبارة i = i - 1. تُختَصر العبارة i = i - 1 على --i. بالعودة إلى المثال مع الدالة bounceBall فإننا زدنا المتغيّر i في كل مرة بواحد. تصبح صيغة التعليمة for على النحو التالي: for (let i = 0; condition; i++) { bounceBall() } لكن… هل يجدُر بي زيادة قيمة المتغيّر أم النقص منها؟ توجد الإجابة في الجزء الأخير من تعليمة التكرار الحلقي for وهو الشرط. في حالة تحقّق الشرط - أي أن قيمته تساوي true - فإن الجملة الموجودة في التعليمة for تُنفَّذ وإلا فلا. ينفّذ مفسر جافاسكريبت عبارة الزيادة incrementalExpression مباشرة بعد تنفيذ الجملة statement ثم يتحقّق مجدّدًا من أن قيمة الشرط تساوي true فإن كان الأمر كذلك نفّذ الجملة statement مرة أخرى. يعيد المفسّر الكرة إلى أن تصبح قيمة الشرط false. حينها يتجاوز الحلقة التكرارية وينفّذ الشفرة التي تليها. إن لم تكن تريد تنفيذ الحلقة فيمكنك جعل قيمة الشرط مساوية لـ false منذ البداية: // لن تُنفَّذ الحلقة التكرارية التالية نظرا لأن قيمة الشرط تساوي false for (let i = 0; i < 0; i++) { bounceBall() const timesBounced = i + 1 console.log('The ball has bounced ' + timesBounced + ' times') } // ستُنفّذ الشفرة التالية فقط console.log('next line of code') ستكون نتيجة تنفيذ الشفرة أعلاه على النحو التالي: next line of code عندما تريد تنفيذ الحلقة التكرارية مرتين فستحتاج إلى تغيير الشرط بحيث تكون قيمته false بعد تنفيذ عبارة الزيادة مرتين: // ستُنفّذ الحلقة التكرارية التالية مرتين for (let i = 0; i < 2; i++) { bounceBall() const timesBounced = i + 1 console.log('The ball has bounced ' + timesBounced + ' times')") } console.log('next line of code') النتيجة: The ball has bounced 1 times The ball has bounced 2 times next line of code أما إذا أردت تشغيل الحلقة عشر مرات فسيتوجّب عليك تغيير الشرط بحيث تصبح قيمته false بعد تنفيذ عبارة الزيادة عشر مرات. // ستُنفّذ الحلقة التكرارية التالية عشر مرات for (let i = 0; i < 10; i++) { bounceBall() const timesBounced = i + 1 console.log('The ball has bounced ' + timesBounced + ' times')") } console.log('next line of code') النتيجة: الحلقات غير المحدودة Infinite loops نجد أنفسنا أمام حلقات غير محدودة عندما تكون قيمة الشرط في الحلقة التكرارية for مساوية دائما لـtrue، ممّا يتسبّب في تجمّد التبويب أو المتصفّح ونحتاج بالتالي لإغلاقه وإعادة تشغيله. استخدام الحلقات التكرارية مع المصفوفات Arrays في الواقع لن تحتاج كثيرا لاستخدام الحلقات التكرارية لتنفيذ دالة عشر مرات كما فعلنا مع الدالة bounceBall السابقة. أغلب استخدامات الحلقات التكرارية هو مع المصفوفات أو الكائنات Objects. يتمثّل التكرار عبر المصفوفة في المرور مرة واحدة على كل عنصُر في المصفوفة. بالإمكان استخدام طول المصفوفة (التابع length) في الجزء الخاص بالشرط من تعليمة التكرار. const fruitBasket = ['موزة', 'تفاحة', 'إجاصة'] // fruitBasket.length is 3 for (let i = 0; i < fruitBasket.length; i++) { console.log("توجد " + fruitBasket[i] + " في السلة") } النتيجة: توجد موزة في السلة توجد تفاحة في السلة توجد إجاصة في السلة هناك طريقة بديلة وهي استخدام عبارة نقص بدلًا من عبارة الزيادة، وذلك بالبدء من نهاية المصفوفة في العبارة الابتدائية ضمن تعليمة التكرار: for (let i = fruitBasket.length - 1; i >= 0; i--) { console.log("توجد " + fruitBasket[i] + " في السلة") } النتيجة (لاحظ الفرق): توجد إجاصة في السلة توجد تفاحة في السلة توجد موزة في السلة المرور على المصفوفات بالحلقة for of هناك طريقة ثالثة - أفضل بكثير - للمرور عبر مصفوفة، وهي استخدام تعليمة التكرار الحلقي for...of وهي تعليمة جديدة من إضافات المعيار ES6. تُكتَب التعليمة بالصيغة التالية const fruitBasket = ['موزة', 'تفاحة', 'إجاصة'] for (let fruit of fruitBasket) { console.log("توجد " + fruit + " في السلة") } النتيجة: توجد موزة في السلة توجد تفاحة في السلة توجد إجاصة في السلة يُفضَّل استخدام التعليمة for ...of بدلا من الطريقة التقليدية لاستخدام تعليمة التكرار الحلقي for عند التعامل مع المصفوفات فهي تمرّ على المصفوفة لمرة واحدة فقط، كما لا تحتاج إلى طول المصفوفة؛ ممّا يجعل قراءة الشفرة وصيانتها أيسر بكثير. يمكن استخدام التعليمة for ...of مع أي كائن لديه الخاصيّة Symbol.iterator. إن طبعت مصفوفة فارغة باستخدام التعليمة console.log فسترى أن لديها خاصيّة باسم Synmbol.iterator (ضمن المفتاح _proto_). العمليات المنطقية في الحلقات التكرارية يمكنك استخدام if/else أو أي عمليات منطقية أخرى داخلة حلقة for التكرارية. فلنفترض مثلا أن لديك مصفوفة بمجموعة من الأعداد وتريد إنشاء مصفوفة ثانية تتضمّن أعداد المصفوفة الأولى التي تصغُر 20. حتى تصل إلى هدفك فيجب أن تمرّ أولا على عناصر المصفوفة الأولى بحلقة for: const numbers = [25, 22, 12, 56, 8, 18, 34]; for (let num of numbers) { // سنكمل التعليمات بعد قليل } ثم نتحقّق ما إذا كان العنصُر الذي نمرّ عليه يقل عن 20. تصبح الشفرة كالتالي: const numbers = [25, 22, 12, 56, 8, 18, 34]; for (let num of numbers) { if (num < 20) { // نكمل بعد قليل } } إذا كان العنصُر أصغر من 20 نضيفه إلى مصفوفة أخرى. نستعمل التابع push لهذا الغرض. تصبح الشفرة كالتالي (smallerThan20 هو اسم المصفوفة الثانية): const numbers = [25, 22, 12, 56, 8, 18, 34]; let smallerThan20 = [] for (let num of numbers) { if (num < 20) { smallerThan20.push(num) } } ترجمة - بتصرّف - للمقال Understanding for loops لصاحبه Zell Liew.
  18. تخيّل أنك تعيش في قرية لا تتوفّر على شبكة مياه. تحتاج، للحصول على الماء، لأخذ سطل فارغ والذهاب إلى بئر وسط القرية وسحب المياه من البئر ثم العودة إلى المنزل. تعاود الكرة مرات عدّة في اليوم، حسب حاجتك للمياه. سيكون شرحُ ما تفعله بالتفصيل في كل مرة تُسأل فيها عن الأمر مضيعة للوقت؛ لذا بدلا من أن تقول “آخذ سطلا فارغا وأذهب إلى البئر وسط المدينة، ثم أسحب دلوا من الماء من البئر وأفرغه في السطل وأعود”، بدلا من ذلك تجيب اختصارا “أجلب الماء”. لقد أنشأت بهذه الإجابة دالة برمجية Function. تعريف الدوالّ الدالة هي كتلة من الأسطر البرمجية التي تنفّذ مجموعة من المهامّ وفق ترتيب محدَّد، مثلا: “آخذ السطل، أذهب إلى البئر، أسحب الماء منه وأعود”. تُعرَّف الدالة بالصياغة التالية: function functionName (parameters) { // نفّذ المهام هنا } function هي الكلمة المفتاحية التي تخبر جافاسكريبت أنك بصدد الإعلان عن دالة. functionName هو اسمُ الدالة. في المثال أعلاه فإن اسم الدالة يمكن أن يكون drawWater (اغرف الماء). يمكن أن يكون اسمُ الدالة أيا اسم تختاره ما دام يحترم شروط أسماء المتغيّرات، أي: أن يكون كلمة واحدة، أن يتكوّن فقط من حروف لاتينية (a-z, A-Z)، أرقام (0-9) أو علامة تسطير سفلي _. أن لا يكون كلمة محجوزة Reserved keywords في جافاسكريبت. المعاملات Parameters هي مجموعة من المتغيّرات تفصل بينها فاصلة لاتينية , تريد تعريفها للاستخدام داخل الدالة. المعاملات اختيارية ويمكن تعريف دالة دون معاملات. استخدام الدوالّ يمكن استخدامُ الدالة بعد تعريفها. تدلّ العبارات “استخدام الدالة”، “تنفيذ الدالة” أو “استدعاء الدالة” على الشيء ذاته. لاستدعاء الدالة نكتُب اسمَها متبوعا بقوسين (). في ما يلي مثال نعرّف فيه دالة باسم sayHello ثم نستخدمها: // تعريف الدالة function sayHello () { console.log('Hello world!') } // استدعاء الدالة sayHello() ينتُج عن تنفيذ الدالة بالطريقة المشروحة أعلاه طباعةُ العبارة Hello world. الإزاحة The indentation يجب إزاحة جميع الأسطُر البرمجية الموجودة في كتلة، أي تقع بين قوسين معكوفين {}، إلى اليمين. هذه الممارسة مهمّة جدا وتساعد في تسهيل قراءة الشفرة البرمجية وصيانتها، وتمكّنك بنظرة واحدة معرفة أن التعليمة console.log('Hello world') هي جزء من الدالة sayHello: function sayHello () { // تعليمة console.log أدناه مزاحة قليلا إلى اليمين وبالتالي فهي جزء من الدالة sayHello console.log('Hello world!') } يمكنك استخدام مسافتيْن (زر المسافة في لوحة المفاتيح مرتيْن) أو مفتاح الجدولة في لوحة المفاتيح Tab لإزاحة التعليمات في الشفرة. يفضّل بعض المطوّرين مفتاح الجدولة في ما يُفضّل آخرون المسافتين. استخدم أيهما تراه مناسبا، مع التأكد من استخدام نفس طريقة الإزاحة في كامل الشفرة. المعاملات تأخذ معظم الدوال معاملات، وهي عبارة عن سلسلة من المتغيّرات تفصل بينها فاصلة ,، تُعرَّف لتُستخدَم داخل الدالة. يمكنك تعريف معاملات بالعدد الذي تريد. function functionName(param1, param2, param3) { // نفّذ المهام هنا } تُسنَد القيم إلى المعاملات بتمريرها بين قوسين إلى الدالة في سلسلة تفصل بينها فاصلة ,. تُسمَّى القيم المُمرَّرة إلى الدالة بالمعطيات Arguments. يُسنَد المعطى الأول إلى المعامل الأول، والمعطى الثاني إلى المعامل الثاني وهكذا: functionName('arg1', 'arg2') فلنشرح الأمر أكثر بمثال. فلنقل إنك تريد تعريف دالة باسم sayName وظيفتها تسجيل الاسم الشخصي والاسم العائلي لشخص. تبدو الدالة بالشكل التالي: function sayName(firstName, lastName) { console.log('اسمك الشخصي هو ' + firstName) console.log('اسمك العائلي هو ' + lastName) } تُعرّف الدالة معاملين هما firstName وlastName. فلنفترض أن الاسم الشخصي هو “محمد” والاسم العائلي هو “عيسى”. لجعل الدالة تعمل على النحو المرغوب نمرّر لها المعطييْن محمد وعيسى كالتالي: sayName('محمد', 'عيسى') نحصُل بعد تنفيذ الدالة بالطريقة أعلاه على النتيجة التالية: اسمك الشخصي هو محمد اسمك العائلي هو عيسى عند تعريف معامل أثناء تعريف الدالة ثم استخدام الدالة دون تمرير معطيات فإن المعامل سيأخذ القيمة “غير معرَّف” undefined: sayName() النتيجة: // اسمك الشخصي هو undefined // اسمك العائلي هو undefined جملة إرجاع Return statement يمكن أن تحتوي الدوال على جملة إرجاع تتكوَّن من الكلمة المفتاحية return متبوعة بقيمة. function functionName () { return 'some-value' } عندما يجد مفسّر جافاسكريبت هذه الجملة فإنه يتوقّف عن تنفيذ التعليمات الموالية في الدالة ويُرجع قيمة (يمرّر القيمة المعنية إلى الشفرة التي استدعت الدالة): function get2 () { return 2 console.log('blah') // لن تُنفذ هذه التعليمة } const results = get2() console.log(results) // تُطبع القيمة 2، وهي القيمة المُرجَعة من الدالة // ملحوظة: لن تُطبَع القيمة 'blah' إن كانت الكلمة المفتاحية return متبوعة بعبارة بدلا من قيمة حرفية فإن مفسّر جافاسكريبت يحدّد نتيجة تنفيذ العبارة ثم يُرجِع القيمة الناتجة. تذكّر أن القيم التي يمكن لجافاسكريبت تمريرها هي إما أنواع أصلية (مثل سلاسل المحارف، الأعداد والقيم المنطقية) أو كائنات Objects (دوالّ، مصفوفات وكائنات). كلّ ما عدا ذلك يجب تقويمه (معرفة قيمته) قبل التمرير إلى الشفرة التي استُدعِيت فيها الدالة. تسلسل تنفيذ التعليمات عند استدعاء الدوال قد تكون الدوال صعبة الفهم على المبتدئين. سنرى، للتأكد من فهم الدوال تماما، مالذي يحدُث عند تعريف دالة واستدعائها. هذه المرة سنتناول كل خطوة على حدة. في ما يلي الشفرة التي سنعمل على تفكيك عملها: function add2 (num) { return num + 2 } const number = add2(8) console.log(number) // تطبع العدد 10 أولا وقبل كل شيء، نحتاج إلى تعريف الدالة قبل أن نتمكّن من استخدامها. يرى مفسّر جافاسكريبت في السطر الأول الكلمة المفتاحية function فيعرف أن الكلمة التي تليها، أي add2 هي اسم الدالة. بعد تعرّف المفسّر على اسم الدالة يتجاوز الشفرة الموجودة بين معكوفين التي تلي الاسم، لأنه حتى الآن لم يُطلب منه تنفيذ الدالة. في السطر التالي يجد المفسّر أننا عرّفنا متغيّرا ذا قيمة ثابتة اسمه number وأسندنا له القيمة add2(8). بما أن الطرف الموجود على يمين علامة الإسناد = هو دالة، فإن جافاسكريبت سيحتاج إلى تنفيذ الدالة قبل إسناد قيمتها إلى المتغيّر. يعيّن المفسّر القيمة 8 لتكون معطى للمعامل num في الدالة ويبدأ بتنفيذ الدالة. عند الدخول إلى كتلة الدالة (الشفرة بين قوسين معكوفين بعد اسم الدالة) يجد المفسّر أمامه الكلمة المفتاحية return متبوعة بعبارة num + 2، فيعرف أنه سيحتاج إلى معرفة نتيجة العبارة قبل الخروج من الدالة. بما أن قيمة num هي 8 فإن num + 2 يجب أن تساوي 10. عرف المفسّر الآن قيمة العبارة num + 2 ويمكنه بالتالي إرجاع القيمة المُتحصَّل عليها إلى الشفرة التي استدعت الدالة. فيضع القيمة المرجعة مكان الدالة فتُصبح 10 مكان add2(8). يُنشئ المفسّر، بعد اكتمال تنفيذ الطرف الأيمن من عملية إسناد المتغيّر والحصول على نتيجتها، متغيّرا بالاسم المذكور number ويعطيه القيمة 10. هكذا يُقرأ تسلسل تنفيذ التعليمات في الدوال. رفع الدوال Hoisting تُنقَل الدوال تلقائيا عند تعريفها بالطريقة التي رأيناها سابقا إلى أعلى الشفرة البرمجية. بمعنى أن طريقتيْ التعريف والاستخدام التاليّتيْن متكافئتان تماما: function sayHello () { console.log('Hello world!') } sayHello() // تُحوّل الشفرة التالية عند التنفيذ تلقائياإلى الشفرة أعلاه sayHello() function sayHello () { console.log('Hello world!') } قد يؤدّي الرفع التلقائي للدوال إلى الإرباك؛ لذا من المناسب تعريف الدوال دائما قبل استخدامها. تعريف الدوال بعبارات الدوال Function expressions توجد طريقة ثانية لتعريف الدوال، وهي عبارات الدوال. يُعرَّف في هذه الحالة متغيّر ويُسنَد إلى دالة غير مسمّاة (دالة مجهولة الاسم Anonymous funvtion) const sayHello = function () { console.log('This is declared with a function expression!') } ينبغي الانتباه إلى أن الدوال المُعرَّفة بعبارات لا تُنقَل تلقائيا إلى أعلى الشفرة. تُنتج الشفرة التالية خطأ: sayHello () // Error, sayHello is not defined const sayHello = function () { console.log('this is a function!') } تُستخدَم الدوال المُعرَّفة بعبارات كثيرا في توابع الكائنات Object methods وفي الدوالّ السهمية Arrow functions. خاتمة الدالة هي كتلة من التعليمات البرمجية تنفَّذ وفق ترتيب محدَّد على منوال: خذ سطلا فارغا، اذهب إلى البئر، اسحب الماء ثم عُد إلى المنزل. تُستدعى الدالة بإضافة قوسين () بعد اسمها. يمكنك عند استدعاء الدالة إضافة معطيات جديدة بذكرها بين القوسين مفصولة بفاصلة ,. يمكن لكلّ دالة أن تحوي جملة إرجاع تعيد قيمة إلى الشفرة التي استُدعِيت منها. يُفضَّل دائما تعريف الدوال قبل استخدامها. ترجمة - بتصرّف - للمقال Understanding JavaScript Functions لصاحبه Zell Liew.
  19. فلنقل إنك تسير في شارع مكتظ وسط المدينة، تتأهب للمرور في مفترق طرق وفي هذه الأثناء انتقلت الإشارة الضوئية الخاصّة بالراجلين إلى اللون الأحمر. مالذي ستفعله؟ تتوقف… أليس كذلك؟ ماذا لو تبدّل اللون إلى الأخضر بعد ذلك؟ تعود إلى المشي. يمكننا اعتماد نفس المبدأ في الشفرات البرمجية. يبدو الأمر كما لو أنك تقول “إن أصبح لون الإشارة أحمر فيجب عليك التوقف وإلا استمرّ في المشي”. هذا بالضبط هو عمل الجملة if/else (إنْ… وإلا) في جافاسكريبت. الجملة if/else تساعد الجملة if/else بالتحكّم في ما يفعله برنامجك في حالات محدّدة. تأخذ الصيغة التالية: if (condition) { // افعل شيئا هنا } else { // افعل شيئا مغايرا هنا } يخبر الشرط Condition الجملة if/else مالذي يجب عليها التحقّق منه قبل الاستمرار. إذا كانت قيمة الشرط صحيحة (تساوي true) فإن جافاسكريبت سينفّذ الشفرة الموجودة داخل كتلة if. أما إذا كانت قيمة الشرط غير صحيحة (أي false) فإن الشفرة الموجودة في الكتلة else هي ما سيُنفّذ. بالعودة إلى مثال الإشارة الضوئية أعلاه فإن الأمر سيأخذ الصيغة التالية: if (الإشارة حمراء) { // توقّف عن المشي } else { // استمرّ في المشي } إن احتجت للتحقّق من شروط عدّة فيمكنك إضافة else if بين كتلتيْ if وelse. متى ستحتاج لشرط ثان؟ فلنقل إنك تمرّ عبر طريق صغيرة. إن لم تكن هناك سيارات فهل ستستمر في الانتظار إلى أن يتغيّر لون الإشارة الضوئية؟ على الأرجح ستواصل طريقك. بترجمة الحالة أعلاه إلى شفرة برمجية نجد التالي: if (الإشارة حمراء) { // توقّف عن المشي } else if (توجد سيارات) { // توقّف عن المشي } else if (شرط آخر) { // افعل أمرا آخر } else { // أمر أخير } إذا كان الشرط الأول في الشفرة أعلاه متحقّقا فإن مفسّر جافاسكريبت ينفّذ الشفرة الموجودة ضمن كتلة if، أما إذا كان هذا الشرط غير متحقّق فإن المفسّر ينظُر في الشرط الموجود في جملة else if الموالية لمعرفة ما إذا كان متحقّقا… وهكذا إلى إن يمرّ عبر جميع جمل else if. يعتمد مفسّر جافاسكريبت على أساسيْن لمعرفة تحقّق الشرط من عدمه: عوامل المقارنة Comparison operators. القيم الصحيحة و القيم الخاطئة. عوامل المقارنة توجد أربعة عوامل أساسية للمقارنة: “أكبر من” < أو “أكبر من أو يساوي”=< “أصغر من” > أو “أصغر من أو يساوي”=> “يساوي تماما” === أو “يساوي” == “يختلف تماما” ==! أو “يختلف” =! النوعان الأولان من عوامل المقارنة واضحان ويستخدمان لمقارنة الأعداد: 24 > 23 // صحيح 24 > 24 // خاطئ 24 >= 24 // صحيح 24 < 25 // صحيح 24 < 24 // خاطئ 24 <= 24 // صحيح النوعان التاليّان يُستخدمان لمقارنة تساوي شيئين: 24 === 24 // صحيح 24 !== 24 // خاطئ إلا أنه يوجد فرق بين “يساوي تماما” === و “يساوي” ==، وبين “يختلف تماما” == ! و “يختلف” =! '24' === 24 // خاطئ '24' == 24 // صحيح '24' !== 24 // صحيح '24' != 24 // خاطئ يتّضح من المثال أعلاه أن مقارنة العدد 24 بسلسلة المحارف 24 تعطي نتيجة خاطئة عند استخدام العامل “يساوي تماما” (===) بينما تعطي نتيجة صحيحة عند استخدام العامل “يساوي” (==). لماذا هذا الاختلاف؟ فلنر الفرق بين “يساوي تماما” و “يساوي”. الفرق بين === و == (و بين ==! و =!) أنواع البيانات Data types في جافاسكريبت ليست صرامة بل متساهلة، عكس لغات أخرى. يعني هذا أننا لا نهتم عندما نعرّف متغيّرا بنوع البيانات الذي ستأخذه قيمة هذا المتغيّر. يمكنك تعريف أي متغيّر وسيتكفّل مفسّر جافاسكريبت بالتعامل مع نوع البيانات الخاصّ بقيمة المتغيّر: const aString = 'Some string' const aNumber = 123 const aBoolean = true عند استخدام العامل “يساوي تماما” (===) أو “يختلف تماما” (==!) فإن مفسّر جافاسكريبت يأخذ أنواع بيانات قيم المتغيّرات بالحسبان؛ لهذا السبب فإن سلسلة المحارف 24 تختلف عن العدد 24. '24' === 24 // خطأ '24' !== 24 // صحيح أما عند استخدام العامل “يساوي” (==) أو “يختلف” (=!) فإن مفسّر جافاسكريبت يحوّل نوع البيانات بحيث يتساوى نوع طرفيْ المقارنة قبل أن ينظُر في القيمة. عمومًا، يحاول مفسّر جافاسكريبت تحويل جميع أنواع البيانات إلى أعداد عند استخدام عوامل المقارنة (ما عدا ===و==!). تُحوَّل سلسلة المحارف 24 في المثال أدناه إلى العدد 24 قبل المقارنة. هذا هو السبب الذي يجعل سلسلة المحارف 24 تساوي العدد 24 عند استخدام العامل ==: '24' == 24 // صحيح '24' != 24 // خاطئ يمكن كذلك تحويل القيم المنطقية (true وfalse) إلى أعداد، وعندها تصبح قيمة true تساوي 1 وfalse تساوي 0: 0 == false // صحيح 1 == true // صحيح 2 == true // خاطئ يعدّ التحويل التلقائي الذي يقوم به مفسّر جافاسكريبت أثناء استخدام عوامل المقارنة أحد أكثر الأسباب شيوعا لصعوبة التعرف على العلل Bugs أثناء تطوير البرامج؛ لذا استخدم دوما عوال المقارنة التامّة (=== أو==!). دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن مقارنة الكائنات والمصفوفات حاول أن تقارن بين الكائنات أو بين المصفوفات بالعامل === أو == وستجد نتيجة مفاجئة: const a = { isHavingFun: true } const b = { isHavingFun: true } console.log(a === b) // خطأ: الكائن a مختلف تماما عن الكائن b console.log(a == b) // خطأ: الكائن a مختلف عن الكائن b الكائنان a وb في المثال أعلاه يبدوان متشابهين تماما: كلاهما كائن ويحويان نفس القيم. الأمر الغريب هو أن المقارنة a === b ترجع دائما قيمة خاطئة false؛ لماذا؟ فلنفترض أنكَ وأخاك (أو أنكِ وأختك) توأم. تبدو مشابهًا تمامًا لأخيك: نفس لون الشعر، نفس شكل الوجه، نفس الثياب؛ كيف يمكن التفريق بينك وأخيك؟ سيكون الأمر صعبًا. لدى كل كائن في جافاسكريبت بطاقة تعريف (هوية) تُسمّى مرجع الكائن Object’s reference. عند استخدام عوامل المقارنة للتحقّق من تساوي كائنين في جافاسكريبت فإنك تطلُب من المفسر التحقّق ممّا إذا كان للكائنيْن نفسُ المرجع (بطاقة التعريف). ليس غريبًا الآن أن تكون قيمة الشرط a === b في المثال أعلاه مساوية لـfalse. فلنعدّل قليلاً على المثال ونسند a إلى b: const a = { isHavingFun: true } const b = a تصبح نتيجة المقارنة a === b الآن مساوية لـ true (أي أن الكائنين متساويان). السبب في ذلك هو أن الكائنين a وb لديهما الآن نفس المرجع. console.log(a === b) // true القيم الصحيحة و القيم الخاطئة إن وضعت اسم متغيّر (hasApples - بمعنى لديه تفاح - في المثال أدناه) مكان الشرط في جملة if/else فإن مفسّر جافاسكريبت سيبحث عن قيمة صحيحة أو قيمة خاطئة. const hasApples = 'true' if (hasApples) { // تناول تفاحة } else { // اشتر تفاحا } القيم الخاطئة هي قيم تأخذ القيمة false عند تحويلها إلى نوع البيانات المنطقية Boolean. توجد ست قيم خاطئة في جافاسكريبت: false undefined null 0 (العدد صفر) "" (سلسلة محارف فارغة) NaN على الجانب الآخر، القيم الصحيحة هي تلك التي تأخذ القيمة true بعد تحويلها إلى بيانات منطقية. في حالة الأعداد فإن أية قيمة مغايرة للصفر تُحوَّل إلى قيمة صحيحة. تُشجّع جافاسكريبت على استخدام التحويل التلقائي إلى قيم صحيحة وقيم خاطئة لكونها تجعل من الشفرة البرمجية أقصر وأسهل فهما. إن أردت على سبيل المثال التحقّق من أن سلسلة محارف فارغة فيمكنك استخدام هذه السلسلة مباشرة في شرط الجملة if/else: const str = '' if (str) { // سلسلة المحارف غير فارغة } else { // سلسلة المحارف فارغة } خاتمة تُستخدم الجمل الشرطية if/else للتحكّم في عمل البرامج في حالات محدّدة؛ فتسمح لك بتحديد ما إذا كان يتوجب عليك الاستمرار في المشي أو الانتظار لتجاوز مفترق طرق. توجد طريقتان للتحقّق من الشروط في جافاسكريبت: عوامل المقارنة القيم الصحيحة والقيم الخاطئة ترجمة - تصرّف - للمقال Understanding if/else statements لصاحبه Zell Liew.
  20. نكتُب شفرات برمجيةً أساسا لحلّ مشاكل. مالذي يحدُث عندما تنقر على زرّ؟ هذه أيضًا مشكلة وينبغي علينا - نحن المطورين - حلّها. نبدأ هذا الدّرس بحلّ مشكلة صغيرة. عدّ التفاحات إن كانت لديك 4 تفاحات واشتريت 27 تفاحة أخرى فكم سيكون لديك من تفاحة؟ اكتُب الإجابة في محرّر النصوص. ما هي إجابتك؟ // هل هي هذه؟ 31 // Or أم هذه؟ 4 + 27 الإجابتان صحيحتان، إلا أن الأخيرة أحسن؛ إذ أنك تترك لجافاسكريبت عبْء الحساب وتخبره كيف يصل إلى النتيجة. ولكن لا يزال عندنا مشكلة في الشفرة السابقة. إنْ نظرت إلى 4 + 27 خارج سياق مشكل التفاحات، فهل ستعرف أننا نعدّ التفاحات التي تحملها الآن؟ الأرجح أن الإجابة هي لا. الأفضل هو استخدام الطريقة الرياضية لإحلال متغيّرات مكان الأعداد 4 و27؛ إن فعلنا ذلك فستكون لدينا القدرة على كتابة شفرة برمجية ذات معنى: initialApples + applesBought initialApples: عدد التفاحات الابتدائي. applesBought: عدد التفاحات المشتراة. تُسمّى عمليّة إحلال متغيّر باسم initialApples مكان العدد 4 بتعريف المتغيّر Declaring a variable. تعريف المتغيّرات تُعرَّف المتغيّرات بالصيغة التالية: const variableName = 'value' توجد أربعة أجزاء في الجملة السابقة يجب الانتباه إليها: اسم المتغيّر variableName القيمة Value علامة الإسناد Assignment = الكلمة المفتاحية const اسم المتغيّر تُشير variableName إلى اسم المتغيّر الذي أنت بصدد تعريفه. يمكنك إعطاء المتغيّر أي اسم تريده بشرط: أن يكون كلمة واحدة أن يتكوّن فقط من حروف لاتينية (a-z, A-Z)، أرقام (0-9) أو علامة تسطير سفلي _ ألّا يبدأ برقم ألّا يكون كلمة محجوزة Reserved keywords في جافاسكريبت إن أردت استخدام أكثر من كلمة في اسم متغيّر فستحتاج لإدماج الكلمات معا وكتابة الحرف الأول من كل الكلمات الموجودة بعد الكلمة الأولى بحرف كبير Capital letter، تُسمّى هذه الطريقة بـ Camel case. المتغيّر applesToBuy مثال على كتابة متغيّر باسم مكوّن من كلمات عدّة. القيمة القيمة هي ما تريد أن يكونه المتغيّر. يمكن للقيمة أن تكون بيانات أوليّة Primitives (مثل سلاسل المحارف Strings والأعداد) أو كائنات (مصفوفات ودوالّ). علامة الإسناد = في جافاسكريبت لا تعمل علامة الإسناد = بنفس طريقة عمل التساوي = في الرياضيات. لذا يتوجّب عدم الخلط بينهما. عندما تستخدم العلامة = في جافاسكريبت فأنت تُسنِد قيمة جزء العبارة الموجود يمين العلامة = تُسنده إلى الجزء الموجود يسار العلامة. في المثال أدناه نُسنِد القيمة 4 إلى المتغيّر initialApples. const initialApples = 4 أو بعبارة أخرى نعطي المتغيّر initialApples القيمة 4. إن طلبت طباعة المتغيّر initialApples فستجد أن النتيجة تساوي 4. التقويم قبل الإسناد لا يُخزّن المتغيّر سوى قيمة وحيدة؛ فإذا كانت لديك معادلة على يمين العلامة = فإن مفسّر جافاسكريبت سيُنفّذ المعادلة الموجودة على الطرف الأيمن ثم بعد ذلك يُسنِد القيمة المُتحصَّل عليها إلى المتغيّر. const initialApples = 4 const applesToBuy = 27 const totalApples = initialApples + applesToBuy سيبدأ جافاسكريبت عند تعريف المتغيّر totalApples في المثال أعلاه بتقويم العبارة initialApples + applesToBuy (التي تعطي النتيجة 31) ثم بعد ذلك يُسنِد القيمة 31 إلى المتغيّر totalApples. الكلمة المفتاحية const توجد ثلاث كلمات مفتاحية لتعريف المتغيّرات، const إحداها. الكلمتان الأخريان هما let وvar. تُستخدَم كل من الكلمات الثلاث لتعريف متغيّرات؛ إلا أنه يوجد اختلاف بين عملها. الفرق بين const وlet وvar أُتيح استخدام كلّ من const وlet في الإصدار السادس من جافاسكريبت ES6، ولهما خصوصيّات مقارنة مع var المتاحة منذ إصدارات سابقة، والتي أصبح استخدامها غير منصوح به. يؤدّي تعريف متغيّر بالكلمة المفتاحية const إلى انعدام إمكانيّة إسناد قيمة جديدة للمتغيّر بعد تعريفه. يعني هذا أن الشفرة التالية ستُنتِج خطأ عند تنفيذها: const applesToBuy = 22 // إعادة إسناد قيمة لمتغيّر عُرِّف بـ const يؤدي إلى خطأ أثناء التنفيذ applesToBuy = 27 أما إن عرّفت متغيّرا بالكلمة المفتاحية let فسيمكنك إسناد قيمة جديدة له دون خطأ: let applesToBuy = 22 applesToBuy = 27 console.log(applesToBuy) أيهما يجب أن أستخدم const أم let؟ في البداية سيكون استخدامُ let بدلا من const أسهل وأقل إثارة للأخطاء؛ إلا أنك مع الوقت وبعد كتابة برامج عدّة ستلاحظ أنه من الأحسن لك التقليل من إمكانية إسناد قيم جديدة للمتغيّرات باستخدام const. ليس هذا الدرسُ التقديمي مناسبا للدخول في تفاصيل أسباب استخدام const لتعريف المتغيّرات. لكن بما أنك ستبدأ في استخدام const أكثر من let عند اكتساب الخبرة بكتابة برامج عدّة بجافاسكريبت فمن الأنسب التعوّد من الآن على هذا الأمر. بالنسبة لـvar فليست لك حاجة في استخدامها، const و let أفضل بكثير. الخلاصة تُستخدَم المتغيّرات في جافاسكريبت للاحتفاظ بقيم. يمكن للمتغيّرات تخزين أي نوع من القيم، سواء كانت بيانات أوليّة أو كائنات. تختلف العلامة = في جافاسكريبت عن علامة التساوي = في الرياضيّات، وتدلّ على الإسناد. ابتعد عن الكلمات المحجوزة لتسمية المتغيّرات واكتب أسماء المتغيّرات المكوّنة من عدّة كلمات وفق أسلوب Camel case. توجد ثلاث كلمات مفتاحية لتعريف المتغيّرات في جافاسكريبت وهي const وlet وvar. استخدم let عندما تريد أن تتاح لك إمكانية التعديل على قيمة المتغيّر. لم تعد توجد حاجة لاستخدام الكلمة المفتاحية var في جافاسكريبت. ترجمة - بتصرّف - للمقال What are variables and why use them in JavaScript لصاحبه Zell Liew حقوق الصورة البارزة محفوظة لـ Freepik
  21. تعرّفنا في مقال سابق على ميزات جديدة في الإصدار ES6 من جافاسكريبت. سنتابع في هذا المقال الحديث عن الميزات الأكثر استخداما من هذا الإصدار وذلك بتناول الإضافات الجديدة التالية: المُعاملان restوspread. تحسينات على الكائنات. القوالب Templates. المعاملان rest وspread يبدو المعاملان rest وspread متشابهين، ويُشار إلى كليهما بثلاث نقاط .... يختلف عمل المعاملين تبعا لطريقة استخدامهما. المعامل rest يعمل rest حرفيا على أخذ بقيّة الشيء ووضعها ضمن مصفوفة. يحوّل المعامل لائحة من المعاملات المحدّدة بفاصلة إلى مصفوفة. فلنر أمثلة عملية على rest. فلنتخيّل أن لدينا دالة باسم add تجمع المعطيات المُمرَّرة لها: sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 55 نعتمد في الإصدار ES5 على المتغيّر arguments في كل مرة نحتاج فيها للتعامل مع دالة تأخذ عددا غير محدّد من المعاملات. المتغيّر arguments هو من النوع Symbol الشبيه بالمصفوفات Array. function sum () { console.log(arguments) } sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) إحدى الطرق التي يمكن استخدامها لجمع قيم arguments هي تحويلها إلى مصفوفة Array باستخدام Array.prototype.slice.call(arguments) ثم المرور على كل عدد باستخدام تابع مصفوفة مثل forEach وreduce. من السهل استخدام forEach لهذا الغرض، لذا سأشرح استخدام reduce: function sum () { let argsArray = Array.prototype.slice.call(arguments) return argsArray.reduce(function(sum, current) { return sum + current }, 0) } يتيح لنا المعامل rest جعل جميع القيم المفصولة بفاصلة في مصفوفة مباشرة: const sum = (...args) => args.reduce((sum, current) => sum + current, 0) أو إن أردنا التقليل من استخدام الدوال السهمية: function sum (...args) { return args.reduce((sum, current) => sum + current, 0) } عرّجنا سريعا عند الحديث عن تفكيك المصفوفات على المعامل rest بسرعة. حاولنا حينها تفكيك المصفوفة scores إلى القيم الثلاث الأعلى: let scores = ['98', '95', '93', '90', '87', '85'] let [first, second, third] = scores console.log(first) // 98 console.log(second) // 95 console.log(third) // 93 إن رغبنا في الحصول على بقية النتائج فبإمكاننا جعلها في مصفوفة مستقلة بالمعامل rest: let scores = ['98', '95', '93', '90', '87', '85'] let [first, second, third, ...restOfScores] = scores console.log(restOfScores) // [90, 97, 95] تذكّر دائما - لتحنب الخلط - أن المعامل rest يجعل كل شيء في مصفوفة؛ ويظهر في معاملات الدوال وأثناء تفكيك المصفوفات. المعامل spread يعمل المعامل spread بطريقة معاكسة لعمل rest. يأخذ المعامل مصفوفة ويوزّعها على لائحة معاملات يُفصَل بين قيمها بفاصلة: let array = ['one', 'two', 'three'] // نتيجة التعليمتين التاليتين متطابقة console.log(...array) // one two three console.log('one', 'two', 'three') // one two three يُستخدَم المعامل spread غالبا لجمع المصفوفات بطريقة تسهّل قراءتها وفهمها. نريد على سبيل المثال جمع المصفوفات التالية: let array1 = ['one', 'two'] let array2 = ['three', 'four'] let array3 = ['five', 'six'] يُستخدَم التابع Array.concat في الإصدارات القديمة من جافاسكريبت لجمع عدد غير محدَّد من المصفوفات: let combinedArray = array1.concat(array2).concat(array3) console.log(combinedArray) // ['one', 'two', 'three', 'four', 'five', 'six'] يتيح المعامل spread توزيع قيم المصفوفات على مصفوفة جديدة على النحو التالي: let combinedArray = [...array1, ...array2, ...array3] console.log(combinedArray) // ['one', 'two', 'three', 'four', 'five', 'six'] يُستخدَم المعامل spread كذلك لحذف عنصُر من مصفوفة دون التعديل عليها. تُستخدَم هذه الطريقة كثيرا في Redux (يشرح هذا الفيديو) كيف يفعلون ذلك. تحسينات على الكائنات الكائنات من الأمور التي يجدر بكلّ مبرمج جافاسكريبت التعوّد عليها. للتذكير؛ تبدو الكائنات بالهيئة التالية: const anObject = { property1: 'value1', property2: 'value2', property3: 'value3', } يضيف الإصدار ES6 ثلاث ميزات جديدة للكائنات في جافاسكريبت: اختصار قيم الخاصيّات Properties، اختصارات للتوابع Methods، إمكانية استخدام أسماء محسوبة للخاصيّات. سنعرّج على كل واحدة من هذه الميزات. اختصار قيم الخاصيّات هل سبق لك ملاحظة أنك تسند أحيانا متغيّرا إلى خاصية كائن تشترك معه في الاسم؟ شيء من قبيل: const fullName = 'Zell Liew' const Zell = { fullName: fullName } قد ترغب في طريقة أكثر اختصارا من السابق بما أن الخاصيّة fullNameتساوي قيمة المتغيّر fullName. تساعد ميزة اختصار قيم الخاصيّات التي يضيفها الإصدار ES6 في تقليل الشفرة اللازمة لكتابة الكائنات عندما يوافق اسمُ المتغيّر اسمَ الخاصيّة: const fullName = 'Zell Liew' // استخدام ميزة الاختصار في ES6 const Zell = { fullName } // يُترجَم الاختصار في الخلفية إلى ... const Zell = { fullName: fullName } اختصارات التوابع التوابع هي خاصيّات بصيغة دوالّ. تُسمّى هذه الخاصيّات توابع لأنها دوال. في ما يلي مثال على تابع: const anObject = { aMethod: function () { console.log("I'm a method!~~")} } يتيح الإصدار ES6 كتابة التوابع بطريقة مختصرة. يمكننا حذف الكلمة المفتاحية function ولن يتغيّر شيء: const anObject = { // حسب ES6 aShorthandMethod (arg1, arg2) {}, // حسب ES5 aLonghandMethod: function (arg1, arg2) {}, } استخدم هذه الميزة لاختصار توابع الكائنات ولا تلجأ إلى الدوال السهمية لكتابة التوابع (راجع الدوال السهمية في مقال سابق). const dontDoThis = { // تجنّب هذا arrowFunction: () => {} } أسماء محسوبة للخاصيّات تحتاج أحيانا إلى أسماء متغيّرة (ديناميكية) لخاصيّات الكائن. في هذه الحالة ننشئ متغيّرا نضع فيه اسم الخاصيّة الديناميكية. تضطرّ في الإصدارات القديمة من جافاسكريبت لإنشاء الكائن ثم إسناد الخاصيّة على النحو التالي: // متغيّر لحفظ اسم الخاصيّة الجديدة const newPropertyName = 'smile' // ننشئ الكائن أولا const anObject = { aProperty: 'a value' } // ثم نسند قيمة للخاصية الجديدة anObject[newPropertyName] = ':D' // إضافة خاصيّة مختلفة قليلا عن السابقة وإسناد قيمة لها anObject['bigger ' + newPropertyName] = 'XD' // النتيجة // { // aProperty: 'a value', // 'bigger smile': 'XD' // smile: ':D', // } ينزع الإصدار ES6 الحاجة للّف والدوران كما في المثال السابق؛ إذ أصبح بإمكانك إسناد أسماء متغيّرة للخاصيّات مباشرة أثناء إنشاء الكائن بجعل الخاصيّة المتغيّرة داخل أقواس معكوفة: const newPropertyName = 'smile' const anObject = { aProperty: 'a value', // أسماء متغيّرة للكائنات [newPropertyName]: ':D', ['bigger ' + newPropertyName]: 'XD', } // النتيجة // { // aProperty: 'a value', // 'bigger smile': 'XD' // smile: ':D', // } القوالب التعامل مع سلاسل المحارف Strings في جافاسكريبت مزعج للغاية. رأينا مثالا على ذلك في دالة announcePlayer عند الحديث عن المعاملات المبدئية. أنشأنا في تلك الدالة سلاسل محارف فارغة ودمجناها باستخدام عامل الجمع +: function announcePlayer (firstName, lastName, teamName) { console.log(firstName + ' ' + lastName + ', ' + teamName) } تأتي القوالب في الإصدار ES6 لتفادي هذا المشكل (كانت القوالب تُسمى سلاسل محارف القوالب Template strings في مسودات ES6). توضع سلاسل المحارف التي نريد جعلها قالبا بين علامتيْ `. يمكن استخدام متغيّرات جافاسكريبت في القوالب داخل ماسك المكان {}$ . هكذا يبدو الأمر: const firstName = 'Zell' const lastName = 'Liew' const teamName = 'unaffiliated' const theString = `${firstName} ${lastName}, ${teamName}` console.log(theString) // Zell Liew, unaffiliated يمكنك كلك إنشاء سلاسل محارف متعدّدة الأسطُر بسهولة. تعمل الشفرة التالية دون مشكل: const multi = `One upon a time, In a land far far away, there lived a witich, who could change night into day` يمكنك كذلك إنشاء شفرات HTML في جافاسكريبت باستخدام القوالب (ربما لا تكون هذه هي أفضل طريقة لإنشاء عناصر HTML، لكنها على كل حال أفضل من إنشاء عناصر HTML الواحد تلو الآخر). const container = document.createElement('div') const aListOfItems = `<ul> <li>Point number one</li> <li>Point number two</li> <li>Point number three</li> <li>Point number four</li> </ul>` container.innerHTML = aListOfItems document.body.append(container) تأتي مع القوالب ميزة الوسوم Tags، وهي دوال يمكن بواسطتها التعامل مع سلاسل المحارف الموجودة في القوالب إن أردت استبدال سلسلة بأخرى. const animal = 'lamb' // هذه الدالة تمثّل وسما const tagFunction = () => { // Do something here } const string = tagFunction `Mary had a little ${animal}` عليّ الاعتراف أنه على الرغم من أن وسوم القوالب تبدو ميزة مفيدة للغاية إلا أنني لم أحتج حتى الساعة لاستخدامها. خاتمة تعرّفنا في هذا الدليل على أكثر ميزات الإصدار ES6 من جافاسكريبت استخداما. سيكون من المفيد التعوّد على استخدام هذه الإضافات كل ما كان ذلك ممكنا، فمن المؤكّد أنها ستجعل شفرتك البرمجية أقصر وأسهل قراءة وبالتالي تزيد من إنتاجيّتك. ترجمة - بتصرّف - للمقال Introduction to commonly used ES6 features لصاحبه Zell.
  22. تعرّفنا في مقال سابق على ميزات جديدة في الإصدار ES6 من جافاسكريبت. سنتابع في هذا المقال الحديث عن الميزات الأكثر استخداما من هذا الإصدار وذلك بتناول الإضافات الجديدة التالية: المعاملات المبدئية التفكيك Destructuring. المعاملات المبدئية تتيح هذه الخاصيّة تحديد معاملات افتراضية عند تعريف الدوال. نأخذ مثالاً لتوضيح الفائدة التي نجنيها من هذا الأمر, سنفترض أننا نريد كتابة دالة تطبع اسم عضو في فريق لعبة. إن كتبنا الدالة حسب الطريقة القديمة - قبل الإصدار ES6 - فستكون شبيهة بالتالي: function announcePlayer (firstName, lastName, teamName) { console.log(firstName + ' ' + lastName + ', ' + teamName) } announcePlayer('Stephen', 'Curry', 'Golden State Warriors') // Stephen Curry, Golden State Warriors يبدو الأمر مناسبا للوهلة الأولى؛ لكن ماذا لو أردنا الإعلان عن اسم لاعب لا ينتمي لأي فريق؟ ستكون نتيجة تنفيذ الدالة أعلاه كالتالي: announcePlayer('Zell', 'Liew') // Zell Liew, undefined طبعا undefined ليس اسم فريق. سيكون من المناسب الإشعار بأن اللاعب غير منتمٍ (Unaffiliated) لأي فريق عند عدم تمرير المعامل للدالة: announcePlayer('Zell', 'Liew', 'unaffiliated') // Zell Liew, unaffiliated يمكننا تحسين الشفرة بالتحقق من تمرير المعامل إلى المتغيّر teamName بدلاً من تمرير القيمة unaffiliated في كلّ مرة: function announcePlayer (firstName, lastName, teamName) { if (!teamName) { teamName = 'unaffiliated' } console.log(firstName + ' ' + lastName + ', ' + teamName) } announcePlayer('Zell', 'Liew') // Zell Liew, unaffiliated announcePlayer('Stephen', 'Curry', 'Golden State Warriors') // Stephen Curry, Golden State Warriors يمكن تقليل الأسطُر باستخدام العوامل Ternary operators المنطقية الثلاثية ?: function announcePlayer (firstName, lastName, teamName) { var team = teamName ? teamName : 'unaffiliated' console.log(firstName + ' ' + lastName + ', ' + team) } يمكن بالاعتماد على ميزة المعاملات المبدئية في ES6 إضافة علامة = أمام اسم المعامل عند تعريف الدالة وسيأخذ المعامل تلقائية القيمة المُحدَّدة عند عدم تمرير قيمة له أثناء استدعاء الدالة. يصبح مثالنا السابق عند كتابته حسب صيغة ES6 كالتالي: const announcePlayer = (firstName, lastName, teamName = 'unaffiliated') => { console.log(firstName + ' ' + lastName + ', ' + teamName) } announcePlayer('Zell', 'Liew') // Zell Liew, unaffiliated announcePlayer('Stephen', 'Curry', 'Golden State Warriors') // Stephen Curry, Golden State Warriors لاحظ أن اسم الفريق يصبح unaffiliated عند عدم تمرير المعامل الأخير إلى الدالة. أمر أخير. يمكنك استخدام القيمة المبدئية للمعامل بتمرير undefined يدويا إلى الدالة. تفيد هذه الطريقة إذا كان المعامل الذي تريد استخدام قيمته المبدئية ليس الأخير في معطيات الدالة: announcePlayer('Zell', 'Liew', undefined) // Zell Liew, unaffiliated التفكيك يعدّ التفكيك طريقة مناسبة لاستخراج قيم من المصفوفات Arrays والكائنات Objects. توجد فروق طفيفة بين تفكيك المصفوفات وتفكيك الكائنات. تفكيك الكائنات فلنفترض أن لدينا الكائن التالي: const Zell = { firstName: 'Zell', lastName: 'Liew' } ستحتاج - للحصول على الاسم الشخصي (firstName) والاسم العائلي (lastName) من Zell إلى إنشاء متغيّريْن وإسناد كل قيمة إل متغيّر: let firstName = Zell.firstName // Zell let lastName = Zell.lastName // Liew يتيح تفكيك الكائن إنشاء هذين المتغيّرين وإسنادهما بسطر واحد: let { firstName, lastName } = Zell console.log(firstName) // Zell console.log(lastName) // Liew مالذي يحدُث هنا؟ تطلُب من جافاسكريبت عند إضافة القوسين المعكوفين على النحو الذي رأيناه أثناء تعريف المتغيّرات تطلُب إنشاء المتغيرات المذكورة وإسناد Zell.firstName إلى firstName وZell.lastName إلى lastName. تترجم جافاسكريبت - الإصدار ES6 - الشفرة التالية: let { firstName, lastName } = Zell إلى: let firstName = Zell.firstName let lastName = Zell.lastName إن سبق استخدام اسم المتغيّر فلن يكون باستطاعتنا تعريفه من جديد؛ خصوصا عند استخدام let أو const. الشفرة التالية غير صحيحة: let name = 'Zell Liew' let course = { name: 'JS Fundamentals for Frontend Developers' // ... other properties } // تتسبّب التعليمة التالية في الخطأ Uncaught SyntaxError: Identifier 'name' has already been declared let { name } = course يمكن تفادي الخطأ في الحالات السابقة بإعادة تسمية المتغيّر أثناء تفكيك الكائن باستخدام النقطتين العموديّتين : على النحو التالي: let { name: courseName } = course console.log(courseName) // JS Fundamentals for Frontend Developers يترجم مفسّر الإصدار ES6 التعليمة عند استخدام النقطتين على النحو التالي: let courseName = course.name أمر أخير بخصوص تفكيك الكائنات. عند استخراج خاصيّة غير موجودة في الكائن فإن المتغيّر يأخذ القيمة undefined: let course = { name: 'JS Fundamentals for Frontend Developers' } let { package } = course console.log(package) // undefined هل تذكر المعاملات المبدئية؟ يمكنك استخدامها لاستخراج المتغيّرات كذلك. الصياغة مطابقة لتلك المستخدمة عند تعريف الدوال: let course = { name: 'JS Fundamentals for Frontend Developers' } let { package = 'full course' } = course console.log(package) // full course يمكنك كذلك إعادة تسمية المتغيّرات مع تحديد قيم مبدئية لها: let course = { name: 'JS Fundamentals for Frontend Developers' } let { package: packageName = 'full course' } = course console.log(packageName) // full course تفكيك المصفوفات يشبه تفكيك المصفوفات تفكيك الكائنات. تُستخدَم الأقواس المُربَّعة [] بدلا من {} لاستخراج متغيّرات من المصفوفة. المتغيّر الأول بين الأقواس يأخذ قيمة العنصُر الأول في المصفوفة والمتغيّر الثاني قيمة العنصر الثاني وهكذا. let [one, two] = [1, 2, 3, 4, 5] console.log(one) // 1 console.log(two) // 2 إن حدّدت متغيرات أكثر من عناصر المصفوفة فإن المتغيرات الزائدة تأخذ القيمة undefined: let [one, two, three] = [1, 2] console.log(one) // 1 console.log(two) // 2 console.log(three) // undefined في الغالب نستخرج العناصر التي نحتاجها من المصفوفة فقط. يمكن استخدام الكلمة المفتاحية rest على النحو التالي لاستقبال بقية المصفوفة: let scores = ['98', '95', '93', '90', '87', '85'] let [first, second, third, ...rest] = scores console.log(first) // 98 console.log(second) // 95 console.log(third) // 93 console.log(rest) // [90, 87, 85] سنخصّص جزءًا من هذا المقال للحديث عن العامل rest، ولكن قبل ذلك سنتحدّث عن ميزة خاصّة عند تفكيك المصفوفات وهي مبادلة المتغيّرات Swapping variables. فلنفترض أن لدينا متغيّريْن a وb: let a = 2 let b = 3 نريد مبادلة هذين المتغيّرين بحيث تكون قيمة a تساوي 3 وقيمة b تساوي 2. نحتاج - في الإصدار ES5 - إلى استخدام متغيّر ظرفي لإكمال المبادلة: let a = 2 let b = 3 let temp // المبادلة temp = a // temp = 2 a = b // a = 3 b = temp // b = 2 فلنر الآن كيفية المبادلة في ES6 بتفكيك المصفوفات: let a = 2 let b = 3; // نحتاج لنقطة فاصلة هنا لأن السطر الموالي يبدأ بقوس معكوف مربَّع // المبادلة باستخدام ميزة تفكيك المصفوفات [a, b] = [b, a] console.log(a) // 3 console.log(b) // 2 تفكيك المصفوفات والكائنات في الدوال أروع ما في التفكيك إمكانيةُ استخدامه في أي مكان تريده. حتى إن بإمكانك تفكيك المصفوفات والكائنات في الدوال. فلنفترض أن لدينا دالة تأخذ مصفوفة من النتائج وتعيد كائنا بالنتائج الثلاث الأعلى. تشبه الدالة التالية ما فعلناه عند تفكيك المصفوفات السابقة: ملحوظة: ليس ضروريا استخدام الدوال السهمية للاستفادة من ميزات ES6 الأخرى. function topThree (scores) { let [first, second, third] = scores return { first: first, second: second, third: third } } توجد طريقة بديلة لكتابة الدالة أعلاه وهي تفكيك المصفوفة أثناء تعريف الدالة. سنقلّل عدد الأسطُر في هذه الحالة كما أننا نعرف أن المعطى المُمرَّر لنا مصفوفة. function topThree ([first, second, third]) { return { first: first, second: second, third: third } } سنرى الآن تفصيلا صغيرا. بما أننا نستطيع الجمع بين المعاملات المبدئية والتفكيك أثناء تعريف الدوال.. فما هي نتيجة الشفرة التالية: function sayMyName ({ firstName = 'Zell', lastName = 'Liew' } = {}) { console.log(firstName + ' ' + lastName) } الأمر معقَّد قليل إذ أننا جمعنا ميزات عدّة في آن. أولا؛ نرى أن الدالة تأخذ معطى واحدا، وهو كائن اختياري تكون قميته {} عند عدم تحديد قيمة. ثانيًّا، نحاول تفكيك الكائن واستخراج المتغيرين firstName وlastName منه واستخدامهما إن وُجدا. أخيرا؛ عندما لا يُعيَّن المتغيران firstName وlastName في الكائن المُمرَّر فإننا نحدّد قيمتيهما بـ Zell وLiew على التوالي. تعطي الدالة النتائج التالية: sayMyName() // Zell Liew sayMyName({firstName: 'Zell'}) // Zell Liew sayMyName({firstName: 'Vincy', lastName: 'Zhang'}) // Vincy Zhang سنتعرّف في مقال لاحق على ميزات أخرى جديدة في الإصدار ES6. ترجمة - بتصرّف - للمقال Introduction to commonly used ES6 features لصاحبه Zell.
  23. تطوّرت لغة البرمجة جافاسكريبت كثيرا خلال السنوات القليلة الماضية. إن كنت تتعلّم جافاسكريبت في 2017 دون أن تتعامل مع الإصدار ES6 فأنت تفوّت الفرصة لاستغلال طريقة يسيرة لقراءة شفرات جافاسكريبت وكتابتها. لا تقلق إن لم تكن خبيرًا في جافاسكريبت، فليس ضروريًا أن تكون ماهرًا جدًا في جافاسكريبت للاستفادة من الامتيازات التي يمنحها الإصدار ES6. سنتعرّف في هذا المقال على ميزات يقدّمها ES6 ويمكنك استخدامها يوميًا للتعوّد على الصياغة الجديدة لجافاسكريبت. الميزات الجديدة في ES6 يقدّم الإصدار ES6 الكثير من الميزات الجديدة ويضيفها إلى جافاسكريبت. يمكنك التعرّف على أهم هذه الميزات بقراءة المقاليْن التاليّيْن: ما الجديد في الإصدار القادم من جافاسكريبت (ECMAScript 6) - الجزء الأول. ما الجديد في الإصدار القادم من جافاسكريبت (ECMAScript 6) - الجزء الثاني. ليس ضروريًا أن تعرف كلّ ما يقدّمه الإصدار الجديد من أول وهلة. سأتشارك معك في هذا المقال ثلاث ميزات للبدء بها واستخدامها: الكلمتان المفتاحيّتان let وconst. الدوال السهمية Arrow functions. بالمناسبة؛ تدعم أغلب المتصفّحات الحديثة - مثل Edge، الإصدارات الحديثة من فيرفكس وكروم - ES6 جيّدًا دون الحاجة لأدوات إضافية مثل Webpack. بالنسبة للمتصفحات القديمة نسبيًا توجد مكتبات بديلة Polyfills يوفّرها مجتمع جافاسكريبت؛ ابحث عنها. ننتقل بعد التقديم إلى الميزة الأولى. الكلمتان المفتاحيّتان let وconst تُستخدَم الكلمة المفتاحية var عادة في ES5 (الإصدار القديم من جافاسكريبت) لتعريف المتغيّرات. يُمكن في ES6 إبدال var بـ let وconst؛ وهما كلمتان مفتاحيّتان لهما تأثير معتبر وتساعدان في تسهيل كتابة الشفرات البرمجية. نبدأ بإلقاء نظرة على الفرق بين let وvar لنفهم لماذا let وconst أفضل. الفرق بين let وvar بما أننا معتادون على var فسنبدأ أولاً بها. يمكننا تعريف المتغيرات بـvar ثم استخدامها بعد ذلك في أي مكان من النطاق Scope الحالي. var me = 'Zell' console.log(me) // Zell عرّفنا في المثال السابق متغيّرا عامًّا Global باسم me. نستطيع استخدام المتغيّر العام me في دالة على النحو التالي: var me = 'Zell' function sayMe () { console.log(me) } sayMe() // Zell إلا أن العكس ليس صحيحًا. إن عرّفنا متغيّرًا باسم me داخل الدالة فلن يمكننا استخدامه خارجها وسيظهر خطأ في التنفيذ: function sayMe() { var me = 'Zell' console.log(me) } sayMe() // Zell console.log(me) // Uncaught ReferenceError: me is not defined يمكننا القول إذًا إن var ذات نطاق معرَّف على مستوى الدالة Function-scoped. يعني هذا أن أي متغيّر عُرِّف داخل دالّة بـvar لن يوجد خارج هذه الدالة. إن عُرِّف المتغيّر بـvar خارج الدالة فسيكون موجودًا في النطاق الخارجي: var me = 'Zell' // النطاق العام function sayMe () { var me = 'Sleepy head' // نطاق الدالة المحلّي console.log(me) } sayMe() // Sleepy head console.log(me) // Zell بالنسبة للكلمة المفتاحية let فهي ذات نطاق معرَّف على مستوى الكتلة Block-scoped. يعني هذا أنه عند إنشاء متغيّر بـlet فستكون موجودة داخل كتلة let. لكن.. ما هي “الكتلة”؟ تُعرَّف الكتلة في جافاسكريبت بأنها كل ما يوجد بين قوسين معكوفين. في ما يلي أمثلة على الكتل: { // نطاق كتلة جديدة } if (true) { // نطاق كتلة جديدة } while (true) { // نطاق كتلة جديدة } function () { // نطاق كتلة جديدة } الفرق بين المتغيرات المعرَّفة على مستوى الدالة وتلك المعرَّفة على مستوى الكتلة كبير جدًا. إن استخدمت متغيّرات معرَّفة على مستوى الدالة فمن السهل تغيير قيمة المتغيّر دون قصد. في ما يلي مثال: var me = 'Zell' if (true) { var me = 'Sleepy head' } console.log(me) // 'Sleepy head' يظهر في المثال السابق أن المتغيّر me يأخذ القيمة “Sleepy head” بعد الخروج من كتلة التعليمة if. ربما لا تواجه مشاكل على النحو المذكور في المثال أعلاه لأنك لا تعرّف متغيّرات بنفس الاسم؛ لكن إن كنت ممّن يستخدم var في حلقات for التكرارية فربما تصادف بعضًا من الأمور الغريبة التي تحدث بسبب آلية التعامل مع نطاقات المتغيّرات في جافاسكريبت. فلنتأمل الشفرة التالية التي تطبع قيمة المتغيّر i أربع مرات ثم تطبعها من جديد مع استخدام الدالة setTimeout التي تنتظر 1000 ملي ثانية (أي ثانية واحدة) قبل تنفيذ التعليمة console.log: for (var i = 1; i < 5; i++) { console.log(i) setTimeout(function () { console.log(i) }, 1000) }; مالذي تتوقّعه من الشفرة السابقة؟ نلاحظ أن المتغيّر i أخذ القيمة 5 في مرات تنفيذ الدالة setTimeout الأربع. كيف أصبحت قيمة i تساوي 5 في كل مرة تُنفّذ فيها الدالة setTimeout؟ يعود السبب في ذلك إلى أن قيمة المتغيّر i أصبحت تساوي 5 حتى قبل تنفيذ الدالة setTimeout لأول مرة. وبما أن var تعرّف المتغيّر على مستوى الدالة فإن i هو نفس المتغيّر الذي تعمل عليه الحلقة التكرارية والذي أصبح يساوي 5 أثناء انتظار setTimeout لانقضاء الأجل المحدّد (1000 ملي ثانية). للحصول على قيمة المتغيّر i التي مُرِّرت إلى الدالة setTimeout (التي تُنفّذ التعليمات متأخرة بثانية عن وقت حصولها على القيمة) فسنتحتاج إلى دالة أخرى - وليكن اسمها logLater للتأكد من أن الحلقة التكرارية for لا تغيّر قيمة i قبل تنفيذ تعليمات setTimeout: function logLater (i) { setTimeout(function () { console.log(i) }) } for (var i = 1; i < 5; i++) { console.log(i) logLater(i) }; نلاحظ طباعة قيم i الصحيحة: 1، 2، 3 و4. من الجيّد أن الأمور الغريبة التي تحدُث مع المتغيرات المعرَّفة على مستوى الدالة - مثل ما حدث مع حلقة for السابقة - يمكن تفاديها باستخدام let. باستطاعتنا إعادة كتابة المثال الأصلي الذي ينادي setTimeout دون الحاجة لكتابة دالة مستقلة، وذلك عن طريق تعريف المتغيرات بـlet: for (let i = 1; i < 5; i++) { console.log(i) setTimeout(function () { console.log(i) }, 1000) }; ستلاحظ عند تنفيذ الشفرة السابقة أن قيم i تأتي حسب المتوقع. تسهّل المتغيرات المُعرَّفة على مستوى الكتلة - كما رأينا - كتابة الشفرات البرمجية بحذف بعض المشاكل التي تواجهها المتغيرات المعرفة على مستوى الدالة. أنصح - لتسهيل الأمور - باستخدام let بدلا من var عند تعريف المتغيّرات في جافاسكريبت. ننتقل بعد أن تعرّفنا على عمل let إلى const والفرق بين الاثنتين. الفرق بين let وconst تتشابه let وconst في أنهما تعرّفان المتغيرات على مستوى الكتلة. يكمن الفرق في أن المتغيرات التي تُعرَّف بـconst لا يمكن تغيير قيمتها بعد تعريفها أول مرة. const name = 'Zell' name = 'Sleepy head' // TypeError: Assignment to constant variable. let name1 = 'Zell' name1 = 'Sleepy head' console.log(name1) // 'Sleepy head' بما أن قيم المتغيرات المعرفة بـconst لا يمكن التعديل عليها فهي مناسبة للمتغيرات ذات القيمة الثابتة. فلنفترض أن لدينا زرا لفتح نافذة منبثقة. أعرف أنه لن يكون هناك سوى زرّ واحد من هذا النوع ولن يُعدَّل عليه. استخدم const في هذه الحالة. const modalLauncher = document.querySelector('.jsModalLauncher') أفضّل دائمًا استخدام const عند تعريف المتغيّرات كل ما كان ذلك ممكنًا، لأني أتأكد من أن قيمة المتغيّر لن يُعدَّل عليها؛ في ما عدا ذلك وفي جميع الحالات المتبقية أستخدم let. الدوال السهمية يُشار إلى الدوال السهمية بالعلامة <=، وهي اختصار لإنشاء دوال مجهولة الاسم Anonymous functions. يمكن استخدام هذه الدوال في أي مكان يُمكن استخدام الكلمة المفتاحية function فيه. على سبيل المثال: let array = [1,7,98,5,4,2] // استخدام دالة مجهولة الاسم حسب الطريقة القديمة (الإصدار ES5) var moreThan20 = array.filter(function (num) { return num > 20 }) // استخدام الدوال السهمية let moreThan20 = array.filter(num => num > 20) تقلّل الدوال السهمية من حجم الشفرة البرمجية وبالتالي تكون هناك أماكن أقل للأخطاء؛ كما تسهّل فهمَ الشفرة البرمجية عند التعود على أسلوبها. فلنر التفاصيل العملية للدوال السهمية. إنشاء الدوال السهمية ربما تكون متعوّدا على إنشاء الدوال في جافاسكريبت على النحو التالي: function namedFunction() { // Do something } ولاستخدامها: namedFunction() توجد طريقة أخرى لإنشاء الدوال وهي إنشاء دالة مجهولة الاسم وإسنادها إلى متغيّر. لإنشاء دالة مجهولة الاسم فإننا نحذف الاسم من تعريف الدالة: var namedFunction = function() { // Do something } توجد طريقة ثالثة فإنشاء الدوال، وهي إنشاءها مباشرة في معامل دالة أخرى؛ وهذه هي الطريقة الأكثر انتشارًا لإنشاء الدوال مجهولة الاسم. في ما يلي مثال: // استخدام دالة مجهولة الاسم في معامل راجع Callback button.addEventListener('click', function() { // Do something }) بما أن الدوال السهمية هي اختصارات للدوال مجهولة الاسم فإنه يمكن إحلالها في أي مكان توجد به دوال مجهولة الاسم. أمثلة: // دالة عادية const namedFunction = function (arg1, arg2) { /* do your stuff */} // دالة سهمية const namedFunction2 = (arg1, arg2) => {/* do your stuff */} // دالة عادية في معامل راجع button.addEventListener('click', function () { // Do something }) // دالة سهمية في معامل راجع button.addEventListener('click', () => { // Do something }) هل لاحظت التشابه؟ في الأساس حذفنا الكلمة المفتاحية function وأبدلناها بالعلامة <= مع تغيير موضعها قليلا. هل تقتصر الدوال السهمية على إحلال <= مكان function؟ أم أن هناك تفاصيل أخرى؟ صيغة كتابة الدوال السهمية في الواقع، تختلف كتابة الدوال السهمية حسب عامليْن هما: عدد المعاملات المطلوبة الحاجة للإرجاع الضمني Implicit return (لنتيجة التنفيذ) العامل الأول هو عدد المعاملات المُممرَّرة إلى الدالة السهمية. يمكنك حذف الأقواس التي تحيط بالمعاملات إن كان لديك معامل واحد, إن لم توجد معاملات فيمكنك إبدال الأقواس () بعلامة تسطير سفلي _. الصيغ التالية كلّها صحيحة لكتابة دالة سهمية: const zeroArgs = () => {/* do something */} const zeroWithUnderscore = _ => {/* do something */} const oneArg = arg1 => {/* do something */} const oneArgWithParenthesis = (arg1) => {/* do something */} const manyArgs = (arg1, arg2) => {/* do something */} العامل الثاني في صيغة كتابة الدوال السهمية هو الحاجة لإرجاع النتيجة ضمنيا. تضيف الدوال السهمية مبدئيًا كلمة return المفتاحية إن كانت شفرة الدالة تمتدّ على سطر واحد فقط ولم تكن مضمّنة في كتلة (بين قوسين معكوفين {...}). ‘الطريقتان التاليتان في كتابة الدوال السهمية متكافئتان: const sum1 = (num1, num2) => num1 + num2 const sum2 = (num1, num2) => { return num1 + num2 } العاملان المذكوران أعلاه هما السبب في كونك تستطيع كتابة المتغيّر moreThan20 بالطريقة المختصرة التي رأيناها سابقًا: let array = [1,7,98,5,4,2] // بدون استخدام ميزات ES6 var moreThan20 = array.filter(function (num) { return num > 20 }) // باستخدام ميزات ES6 let moreThan20 = array.filter(num => num > 20) بالمختصر، الدوال السهمية رائعة. ستحتاج لبعض الوقت حتى تتعوّد عليها. حاول استخدامها ومع الزمن ستعتادها. أريد قبل إنهاء الحديث عن الدوال السهمية تعريفك بتفصيل عمليّ آخر يسبّب الكثير من الخلط عند استخدام هذه الدوال. المتغيّر this تختلف قيمة المتغيّر this المُعرَّف مسبقا حسب طريقة استدعائه. تكون قيمة المتغيّر this الكائن Window عند استدعاء المتصفّح له خارج أي دالة. console.log(this) // Window عند استدعاء المتغيّر داخل دالة بسيطة فإن قيمته ستكون الكائن العام Global object (أي الكائن Window عندما يتعلّق الأمر بالمتصفّح). function hello () { console.log(this) } hello() // Window تسند جافاسكريبت دائما الكائن Window إلى المتغيّر this عندما يُستدعى من دالة بسيطة (مثل setTimeout). عند استخدام المتغيّر داخل تابع Method فإن القيمة تكون كائن التابع: let o = { sayThis: function() { console.log(this) } } o.sayThis() // o يحيل المتغيّر this إلى الكائن المُنشَأ حديثا عند استدعائه داخل دالة مشيّدة Constructor: function Person (age) { this.age = age } let greg = new Person(22) let thomas = new Person(24) console.log(greg) // this.age = 22 console.log(thomas) // this.age = 24 أما عند استخدام المتغيّر this داخل مستمع لحدث Event listener فإن قيمته تكون العنصُر الذي أطلق الحدث: let button = document.querySelector('button') button.addEventListener('click', function() { console.log(this) // button }) يظهر من الأمثلة السابقة أن قيمة this تحدّدها الدالة التي تستدعيه. تعرّف كل دالة قيمة خاصة بها لـthis. لا تُربَط this في الدوال السهمية بقيمة جديدة أبدا، مهما كانت طريقة استدعاء الدالة. تبقى قيمة المتغيّر this مساوية دائما لقيمة this في السياق الذي توجد به الدالة السهمية. يبدو الأمر مربكًا نوعًا ما، لذا سنأخذ بضعة أمثلة للشرح. أولا؛ لا تستخدم أبدا دوال سهمية لتعريف توابع الكائنات؛ لأنك لن تستطيع حينها الإحالة إلى الكائن باستخدام المتغيّر this: let o = { // تجنّب تعريف التوابع بدوال سهمية notThis: () => { console.log(this) // Window this.objectThis() // Uncaught TypeError: this.objectThis is not a function }, // استخدم دوال عادية لتعريف التوابع objectThis: function () { console.log(this) // o }, // يمكن أيضا استخدام الاختصار التالي لتعريف التوابع objectThis2 () { console.log(this) // o } } ثانيًّا؛ من المستحسَن ألا تستخدم الدوال السهمية لإنشاء مستمعات لأحداث لأن this لن تحيل إلى العنصُر الذي ربطت به المستمع. إن فعلت فيمكنك الحصول على السياق الحقيقي لـthis باستخدام event.currentTarget: button.addEventListener('click', function () { console.log(this) // button }) button.addEventListener('click', e => { console.log(this) // Window console.log(event.currentTarget) // button }) ثالثًا؛ قد تحتاج لاستخدام this مع الدوال السهمية في أماكن تتبدّل فيها قيمة المتغيّر بدون رغبتك. الدالة setTimeout مثال على ذلك. بهذه الطريقة لن تحتاج للتعامل مع مشاكل this، that وself الاعتيادية: let o = { // Old way oldDoSthAfterThree: function () { let that = this setTimeout(function () { console.log(this) // Window console.log(that) // o }) }, // Arrow function way doSthAfterThree: function () { setTimeout(() => { console.log(this) // o }, 3000) } } الاستخدام السابق مفيد جدًا عندما تحتاج لحذف صنف Class أو إضافته بعد انقضاء مدة معينة: let o = { button: document.querySelector('button') endAnimation: function () { this.button.classList.add('is-closing') setTimeout(() => { this.button.classList.remove('is-closing') this.button.classList.remove('is-open') }, 3000) } } في الختام، استخدم الدوال السهمية في أي مكان آخر لتجعل شفرتك البرمجية أنظف كما في مثالنا السابق moreThan20: let array = [1,7,98,5,4,2] let moreThan20 = array.filter(num => num > 20) سنتعرّف في مقال لاحق على ميزات أخرى جديدة في الإصدار ES6. ترجمة - بتصرّف - للمقال Introduction to commonly used ES6 features لصاحبه Zell.
  24. تزداد شعبية Go، وهي لغة برمجة حديثة تطوّرها شركة Google، تزداد كثيرا في التطبيقات والشركات؛ كما توفّر مجموعة متناسقة من المكتبات البرمجية. يشرح هذا الدرس خطوات تثبيت الإصدار 1.8 (الإصدار المستقر الأحدث حتى الآن) على توزيعة لينكس أوبونتو 16.04. سننفّذ في الخطوة الأخيرة من هذا الدرس تطبيق “أهلا بالعالم” صغيرا للتأكد من تثبيت مصرّف اللغة Compiler وعمله. المتطلّبات يفترض هذا الدرس توفّر نظام أوبونتو 16.04 معدًّا للعمل مع مستخدم إداري غير المستخدم الجذر بالطريقة التي يشرحها الإعداد الابتدائي لخادوم أوبونتو. الخطوة الأولى: تثبيت Go نبدأ بتثبيت Go على الخادوم. اتّصل - إن دعت الحاجة لذلك - بالخادوم عن طريق SSH: ssh sammy@your_server_ip اذهب إلى صفحة التنزيلات على الموقع الرسمي لـGo واعثُر على رابط الملف المضغوط لآخر إصدار مستقر، مع قيمة تجزئة SHA256 الخاصة به. تأكد من أنك في المجلّد الشخصي للمستخدم ثم نزّل الإصدار انطلاقا من الرابط الذي تحصّلت عليه في الفقرة الماضية: cd ~ curl -O https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz استخدم الأمر sha256sum للتحقّق من الملف المضغوط: sha256sum go1.8.3.linux-amd64.tar.gz مثال على المُخرجات: 1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz ستحصُل على قيمة تجزئة Hash مثل تلك الموجودة في المُخرجات السابقة. تأكد من أنها توافق قيمة التجزئة الخاصة بالملف التي تحصّلت عليها من صفحة التنزيلات. سنستخدم الآن الأمر tar لفك ضغط الملف. يطلُب الخيار x استخراج محتوى الملف المضغوط، يُظهر الخيار v مخرجات مفصَّلة ويحدّد الخيار f أننا سنمرّر للأمر tar اسم الملف المضغوط: tar xvf go1.6.linux-amd64.tar.gz ستحصُل الآن على مجلّد باسم go في المجلّد الشخصي للمستخدم. نفّذ الأمرين التاليين لتعديل ملكية المجلّد go ثم نقله إلى المسار usr/local/ : sudo chown -R root:root ./go sudo mv go /usr/local ملحوظة: المسار usr/local/go/ هو المسار المنصوح به رسميا لتثبيت Go إلا أن بعض الحالات قد تتطلّب تثبيته على مسار مختلف. الخطوة الثانية: ضبط مسارات Go سنضبُط في هذه الخطوة المسارات الخاصّة بـGo في بيئة النظام. نفتح الملف profile./~ لتحريره: sudo nano ~/.profile نضيف السطرين التاليّين في نهاية الملف لضبط قيمة المتغيّر GOPATH، الذي يحدّد المسار الذي يجب على المصرّف البحثُ فيه عن الملفات المكتوبة بـGo، ولإضافة هذا المسار إلى متغيّر النظام PATH: export GOPATH=$HOME/work export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin أضف الأسطر أدناه إلى الملف بدلا من الأسطر السابقة إن اخترت مسارا غير الذي اخترناه لتثبيت Go. يفترض المثال أن Go مثبَّت في المجلّد الشخصي للمستخدم: export GOROOT=$HOME/go export GOPATH=$HOME/work export PATH=$PATH:$GOROOT/bin:$GOPATH/bin نغلق الملف بعد التحرير ونعتمد التغيير بتنفيذ الأمر source: source ~/.profile الخطوة الثالثة: اختبار التثبيت نتأكّد بعد أن ثبّتنا Go وضبطنا مساراته من عمله. أنشئ مجلّدا جديدا لحفظ ملفات Go. مسار هذا الملف هو نفس المسار الذي حدّدناه في المتغيّر GOPATHضمن الخطوة السابقة: mkdir $HOME/work أنشئ مجلّدات مشاريع Go ضمن هذا المجلد كما في المثال التالي. يمكنك إبدال user في المسار أدناه باسم المستخدم الخاصّ بك على GitHub إن كنت تخطّط لاستخدام Git لإيداع شفرتك البرمجية على GitHub. إن لم تكن تخطّط لذلك فيمكن اختيار تسميات أخرى مثل my_project. mkdir -p work/src/github.com/user/hello ننشئ ملفّ Go بسيطا للتجربة، ونسميه hello: nano ~/work/src/github.com/user/hello/hello.go ألصق شفرة Go التالية ضمن محرّر النصوص. تستخدم هذه الشفرة حزمة main في Go، تستورد مكتبة fmt لدوالّ الإدخال والإخراج وتضبُط دالة جديدة لطباعة الجملة hello, world. package main import "fmt" func main() { fmt.Printf("hello, world\n") } يطبع البرنامج السابق عند تنفيذه بنجاح العبارة hello, world، وهو ما يدلّ على نجاح تصريف Compiling شفرة Go. احفظ الملف ثم أغلقه؛ ثم صرّفه باستدعاء الأمر go install: go install github.com/user/hello يمكننا الآن تشغيل البرنامج بتنفيذ الأمر hello: hello إن تمّ كل شيء على ما يُرام فستُطبَع العبارة hello, world. يمكنك معرفة أين يوجد الملف التنفيذي للبرنامج (ناتج التصريف) بتمرير اسمه إلى الأمر which: which hello مثال على المُخرجات: /home/user/work/bin/hello خاتمة يصبح لديك بعد تنزيل حزمة Go وتثبيتها وضبط مساراتها نظام جاهز لاستخدامه في التطوير بلغة Go. يمكنك الآن البدء بتعلم كتابة البرامج بهذه اللغة، راجع قسم البرمجة بلغة Go للمزيد. ترجمة - بتصرّف - للمقال How to Install Go 1.6 on Ubuntu 16.04 لصاحبه Brennen Bearnes.
  25. نعرف نحن مطوّري البرامج أهميّة اتّباع أفضل الممارسات الأمنية؛ إلا أننا نتسرّع غالبا في تنفيذها، ربما بسبب ما تستدعيه من العمل الجادّ حتى ترسخ في الأذهان. يحدُث أحيانا أن ترى ممارسة أمنية شديدة الخطورة لدرجة أنها تبقى محفورة في ذهنك. أمرّ في عملي مدير أنظمة على الكثير من الممارسات الأمنية الخاطئة، إلا أن الثلاثة التي سأتحدّث عنها في هذا المقال هي أساسيّات يجب على كلّ مطوّر برامج تفاديها. أنبّه هنا إلى أنني أرى كل واحدة من الممارسات المذكورة لدى شركات كبيرة ومطوّرين ذوي خبرة طويلة، لذا فليس صحيحا لصقُها بالمطوّرين المبتدئين. لا تستخدم التعمية لكلمات السّر .. بل التجزئة Hash عملتُ في وقت سابق من مسيرتي المهنية مع شركة تستخدم نظام إدارة يخزّن بيانات شديدة الأهميّة، وفي أحد الأيام طُلِب مني إجراء مراجعة أمنية للشبكة والبرنامج الذين تعتمد عليهما بياناتنا الحرجة. قضيتُ بضع دقائق في البحث ثم قرّرت تشغيل Wireshark لرؤية حركة البيانات عبر الشبكة. استخدمتُ حاسوب العمل للدخول إلى نظام المعلومات ولاحظتُ أمرا غريبا. رغم أن هذه الحادثة كانت قبل انتشار SSL إلا أنني لم أكن أتوقّع أن أرى نصوصا واضحة تحوي حقولا مثل username (اسم المستخدِم) وpassword (كلمة السرّ). بدا بعد التدقيق أن النظام كان يُرسِل اسم المستخدم الخاصّ بي وسلسلة محارف عشوائية - لم تكن كلمتي للسرّ - عبر الشبكة. لم أستطع ترك الأمر على تلك الحال، فحاولتُ تسجيل الدخول مجدّدا إلا أنني هذه المرة أدخلتُ - عن قصد - كلمة سرّ خاطئة. لم أغيّر كلمة السّر كليّةً، بل اكتفيتُ بتغيير محرف واحد فقط. كنتُ أتوقّع رؤية سلسلة محارف جديدة مختلفة تمامًا تمثّل كلمتي للسّر تمرّ عبر الشبكة. بدلا من ذلك، لم يتغيّر سوى أول محرفيْن من سلسلة المحارف. كان الأمر مثيرًا للانتباه، فرغم أن خبرتي كانت متواضعة نوعا ما، إلا أنني كنتُ أعرف أنه إن طُبِّقت دالة تجزئة Hash بطريقة صحيحة على كلمتي للسرّ فستكون سلسلة المحارف مختلفة تماما، وليس فقط أول محرفين. ياللهوْل.. حتى مخطّط تعميّة (تشفير) جيّد كان سيُنتج سلسلتيْ محارف مختلفتين تماما، وهو ما يبدو أن مخطّط التعمية المستخدَم لا يقوم به. جرّبتُ كلمتي سرّ أخرييْن. تسلّحتُ بأوراق وقلم رصاص وقضيتُ الساعتيْن المواليتيْن في محاولة العثور على مخطّط فكّ التعمية. كان لديّ بانتهاء هاتيْن الساعتيْن سكريبت بايثون يمكنه أخذ أي واحدة كلمات السّر “المعمّاة” تلك ثوم فكّ تعميّتها وكشف كلمة السّر الأصلية؛ أمر يُفترَض أن لا أحد بإمكانه فعله. أنا متأكّد من أنه لم يدُر بخلد الشخص الذي وضع ذلك المخطَّط أن أحدا سيجلس ساعتيْن ويعمل على تفكيك مخطّطه؛ إلا أني فعلتُ ذلك. لماذا؟ لأنه كان بإمكاني ذلك. لا تعمّي كلمات السّر إن اضطررت لتخزينها من أجل المقارنة، فهناك دائما إمكانية أن يستطيع أحدهم إيجاد خوارزميّة أو مفتاح لفك التعميّة. لا يوجد عكس مباشر للتجزئة، بمعنى أنه لا يمكن لأحد الحصول على الأصل إلا إذا كان لديه جدول يربط بين النص الواضح وتجزئته (أو أنه خمّنه). معرفة آلية التجزئة وطريقة عملها لا تضرّ بسلامة البيانات، في حين يحدُث ذلك عند معرفة مخطّط التعمية ومفتاحها. 2. لا تترك منافذ خلفية Backdoors سريّة في البرامج كنتُ في وظيفة سابقة لدى إحدى شركات الخدمات البرمجية أقدّم الدعم لمستخدمين أخبروني أن أسماء المستخدمين التي بحوزتهم لم تعد تعمل. كان الدعم جزءًا من خدمة مدفوعة تقدّمها الشركة المطوّرة للبرنامج المستخدَم. خطر ببالي، قبل محاولة معرفة المشكل الكامن وراء إحدى أكثر مكالمات الدعم الفني إضجارا (“بيانات الدخول الخاصة بي لا تعمل”)، أن أجرّب تسجيل الدخول بنفسي. بالفعل لم تكن أسماء الدخول تعمل. كان النظام منصةً تعليمية مبنية على تقنيات الوِب، وكنا قد دفعنا مقابل وظائف محدودة من قدراتها الكثيرة. لفت أمر نظري بينما كنتُ أبحث في صفحة تسجيل الدخول. بدا حرف في إحدى المحارف ذا شكل مختلف قليلا عن البقية. ربما كان السببُ استخدام خط مختلف عن بقية الصفحة. عرضتُ مصدر الصفحة ولاحظتُ وجود رابط على هذا الحرف بالضبط. كان الرابط مخفيًّا عن قصد ولم تكن حالة المؤشّر تتغيّر عندما يحوم على الرابط. فتحتُ - بحذر شديد - الرابط في نافذة متصفّح جديدة. فجأةً بدت أمامي شاشة تفصّل معلومات عن مجموعة كاملة من الحواسيب، وتعطيني التحكّم الكامل في ما يمكن لهذه الحواسيب أن تعمله. كان بمقدوري إطفاء هذه الحواسيب، إعادة تشغيلها، أخذ لقطات من الشاشة.. أي شيء. هاتفتُ الشركة المطوّرة للبرنامج وطلبتُ الحديث مع مسؤول التقنية لديهم. تحدّثتُ في الأخير بعد المرور على أشخاص عدّة مع مَن يبدو أنهم يفهم ما أتحدّث عنه. أجاب “آه.. فعلا”، وأكمل “أضفنا ذلك الرابط ليسهل علينا الوصول. ولا أحد - قبلك - أبدا عثر عليه. سنحذفه فورا”. سألني قبل أن ننهي المكالمة سؤالا أخيرا: “لماذا بدأت في النظر إلى شفرات HTML في صفحة الدخول؟” كانت إجابتي بسيطة: “لأنني أستطيع ذلك”. لا يستحق وضعُ منفذ خلفي في نظام ما أي درجة من المخاطرة.. سيعثُر عليه شخص ما في نهاية المطاف. مهما كانت درجة الغموض فإن تحليل الشفرات البرمجية - كذلك البحث والحافز عموما - يحمل في طيّاته أكثر النتائج غرابة وفجائية. 3. استوثق من المستخدمين على جميع الصفحات.. وليس فقط صفحة الدخول كنتُ في مرحلة سابقة من مسيرتي المهنية جزءًا من مشروع تطوير برمجي كان يتولّى تنفيذه مطوّر متمرّس. كنتُ أحسّ بعدم الارتياح مع هذا التطبيق خصوصا، فأخبرتُ مديري بأننا نحتاج لإجراء مراجعة أمنية معمَّقة للشفرة البرمجية. طُلِب مني أن أنظُر في التطبيق بحثا عمّا يمكنني العثور عليه. بدأتُ بالتجوّل في التطبيق، تسجيل الدخول، وعرض بعض البيانات. ثم لاحظتُ أمرا بدا لي مثيرا للاهتمام. إن علمتُ Bookmarked رابطا بعد تسجيل الدخول والتجول في النظام فإن بإمكاني نسخه ثم لصقه في متصفّح آخر وسأحصُل على نفس الصفحة المُعلَّمة، دون الحاجة لتسجيل الدخول. سألتُ المطوّر “لماذا لا تتحقّق في كل صفحة من أن المستخدم مسجَّل الدخول؟ إذ يكفي أن أحصُل على رابط بعد تسجيل الدخول ونسخه ويمكنني الوصول إلى هذه الصفحة متى أردت دون الحاجة لتسجيل الدخول”، فسألني “لماذا تفعل ذلك؟”. أجبتُه: “لأنه يمكنني ذلك”. لا تترك أي شيء للصدفة حتى المطوّرون المتمرّسون يقعون في هذه الأخطاء؛ فهم يظنّون ألا أحد سيتعمّق في نظام لا يحقّ له الوصول إليه. المشكلة أن المستخدمين سيتسكّعون في النظام وسيعثرون على هذه الثغرات في النهاية. النصيحة الأهم التي يمكن لشخص مثلي، مجرّد هاو لمجال الحماية، أن يقدّمها هي:لا تترك أي شيء للصدفة. يوجد أشخاص - مثلي - يحبون التعمق في الأشياء لمعرفة كيف تعمل ولماذا. ولكن يوجد آخرون ربما أكثر خبرة ومعرفة سيتعمّقون في الأنظمة بحثا عن اكتشاف الثغرات ولاستغلالها. لماذا؟ لأن باستطاعتهم فعل ذلك. ترجمة - بتصرّف - للمقال 3 security tips for software developers لصاحبه Pete Savage. حقوق الصورة البارزة محفوظة لـ Freepik
×
×
  • أضف...