البرمجة غرضية التوجه المتقدمة في php

البرمجة غرضية التوجه المتقدمة في php

 

السلام عليكم ورحمة الله وبركاته

كثيراً ما نبحر بعيداً عن شطئاننا وأراضينا وقٌرانا , بحثاً عن عالم جديد وأفكار جديدة وأفق آخر , وقد تطول غربتنا عن أرضنا التي نعشق , ولكننا في النهاية نجد أنفسنا رهيني شوقنا لتلك الأرض , ولم نعد نطيق الابتعاد عنها أكثر من ذلك .

تمر بنا أحيان كثيرة ننكر فيها رغبتنا في العودة …ونتحدى أنفسنا ولهفتنا لذلك , ونكابر ونراهن على ذلك …في تلك اللحظات – التي قد تكون مصطنعة أو حالة راهنة واقعية – نعتقد بأن ما نقوله هو الذي سيتم فعليا ً. ولكن ما إن ينبض الشوق الحقيقي في عروقنا , حتى نجد أنفسنا تحترق بحرارة الشوق …شوق العودة , وتكسر كل القوانين والضوابط التي وضعتها سابقاً تحسباً من هكذا مرحلة ….

وهذا هو حالي مع تدوينتي هذه , فقد تأخذني فورة الحياة , وروتين العمل , وضيق الوقت بعيداً عن مدونتي التي أحب , ولكن ما ألبث أن أعود إليها وكلي شوق وحنين لأن أغنيها بما هو جديد , وبالأشياء التي علمتني إياها الحياة ,عسى ولعل أن يستفيد منها أحد آخر.

أما الآن , وبعد هذه المقدمة التي أستمتع دوماً بكتابتها في مقدمة أي موضوع , سنباشر الآن رحلتنا الممتعة في مجال البرمجة غرضية التوجه المتقدمة  و php  …

 

البرمجة غرضية التوجه مع مكتبة php  النظامية  SPL (standered php library )في الإصدار الخامس من php  

مقدمة :

تغطي هذه المقالة كل التوسعات المضافة إلى البرمجة غرضية التوجه في الإصدار الخامس من PHP بما فيها من SPL (مكتبة php  النظامية Standard PHP Library ) وكيفية استخدامهم في تطبيقاتنا الفعلية, وذلك حتى لا يبق الموضوع قيد الدراسة النظرية.

ملاحظة :

حتى تستطيع الاستفادة من قراءة هذه المقالة , يجب أن يكون لديك فكرة عن الصفوف classes, والواجهات interfaces , والأعضاء السكونين –الثابتين – static members , والمراجع references. أي باختصار يجب أن يكون لديك فكرة بشكل عام عن مفاهيم البرمجة غرضية التوجه.

كما يتوجب أن يكون لديك فكرة وفهم عن بعض قوالب التصميم الأساسية basic design patterns مثل قالب المعمل Factory.

سيتم استعراض هذه المفاهيم وشرحها من خلال استخدام طبقة قاعدة المعطيات المفاهيمية conceptual database layer.

 

غالباُ في برمجة تطبيقات الوب , يكون هنالك ميل كبير ونزعة لدى المبرمجين لكتابة وبرمجة طبقة مجردة ومنفصلة خاصة بقاعدة المعطيات , وذلك لأهداف تتعلق بسهولة نقل البرنامج – تطبيق الوب – من بيئة لأخرى database portability , وكمثال عليها PEAR::DB abstraction.

ولكن لسوء الحظ , فإن لا يمكن كتابة كود عام يتعامل مع كل قواعد المعطيات , وبالتالي فهو سيكون موافقاً للعمل مع قاعدة معطيات وحيدة .

والسؤال هنا هو التالي:

كيف بإمكاننا التخلص من هذه المحدودية ؟

الجواب يكمن في استخدام القوة التي يقدمها لنا الإصدار الخامس من PHP بما فيه من الواجهات والمكتبة النظامية الخاصة ب PHP Interfaces and SPL , التي تزودنا بقدر عالي من الأداء الأمثلي , وتمكننا من التمتع بخاصية توافق رمازنا – الكود – مع البيئات المختلفة portability (المحمولية) , وبذلك يصبح بإمكاننا- مع إجراء بعض التعديلات البسيطة – تشغيل تطبيقاتنا على قواعد معطيات مختلفة.

الواجهات في PHP5 PHP5 Interfaces in 

مع وجود عدد من الابتكارات المترافقة في PHP5 و SPL ( مكتبة PHP النظامية),أصبح الآن بإمكانك استخدام المفاهيم المتقدمة للبرمجة غرضية التوجه, مثل الصفوف classes, الواجهات interfaces, والدلائل indexers, والعدادات iterators, بطريقة لم تكن تحلم بها من قبل , ولم تكن متاحة أصلاً.

إن SPL ( اختصار لمكتبة PHP النظامية Standard PHP Library) هي بمثابة تمديد ل php , حيث تضيف لنواة php كل من الواجهات interfaces والصفوف classes والتوابع functions.

إن كل من التمديدات التي تم ذكرها , ولحد هذه اللحظة , لا تزال غير موثقة بشكل جيد , ولذلك سأحاول شرح استخدام الواجهات الثلاث الموجودة في  SPL والتي نحن بحاجة لاستخدامها حتى ننهي هذا المشروع.

الواجهات الثلاث هي التالية:

The ArrayAccess interface

The Countable interface

The Iterator interface

وسنبدأ ب

The ArrayAccess interface 

عندما تم تنجيز implement هذه الواجهة , فقد تم تمكين المبرمجين من استخدام الدلائل indexers عليها (أي استخدام عمليات دليل المصفوفة [])وذلك بالنسبة لأي غرض لا على التعينن ( أي بإمكاننا النظر إليها على أنها مصفوفة وعناصرها هي الأغرض وبالتالي بإمكاننا الوصول إلى هذه الأغراض عبر اسخدام العمليات المتاحة على الأدلة index operators []).

كل الأغراض المفهرسة indexed objects  في PHP تحتاج بشكل أساسي إلى 4 طرائق methods  حتى تستطيع التعامل معها كمصفوفة , ويجب أن تصرح على أنها تحقق الواجهة ArrayAccess (implement the ArrayAccess interface).

الطرائق الأربع التي يحتاجها كل غرض مفهرس هي التالية:

offsetExists($offset)

تستخدم هذه الطريقة لأخبار php عن وجود قيمة value   (أو عدم وجودها )بالنسبة للمفتاح key  المحدد عبر الإزاحة المعطاة والممرة كعنصر دخل لهذه الطريقة ($offset)

القيمة المرجعة من هذه الطريقة هي إما القيمة المنطقية true أو false.

offsetGet($offset)
تستخدم هذه الطريقة لإعادة القيمة value  المحددة عبر المعامل –المفتاح key – الممر كدخل لهذه الطريقة offset.

offsetSet($offset, $value)

تقوم هذه الطريقة بتوضيع القيمة value  الممرره كمعامل دخل لهذه الطريقة ضمن الغرض وذلك في المكان المحدد لها عبر المفتاح offset  الممرر أيضاً كمعامل دخل .

بالإمكان إطلاق استثناء من هذه الطريقة في حال كانت هذه المجموعة للقراءة فقط read-only collection, وبالتالي ليس بإمكانك تعديلها بكتابة قيم جديدة فيها.

offsetUnset($offset)
تستخدم هذه الطريقة في الحالات التي يتم فيها إزالة قيمة من المصفوفة , إما عن طريق unset() أو  عبر إسناد قيمة اللاشيء value of null إلى المفتاح key الخاص بقيمة ما.

في حالة المصفوفات العددية,لا يتوجب حذف المفتاح منها , ولا يتوجب إعادة فهرسة تلك المصفوفات ,إلا في حال كان ذلك لغاية مدروسة في نفسك.

 The Countable interface

تحاكي هذه الواجهة سلوك التابع الشهير count() , وبذلك تعيد حجم الغرض الذي قمت بتعريفه , وذلك بالاعتماد على الكود الذي ستقوم أنت بكتابته ضمن هذه الطريقة.

تعتمد هذه الواجهة على الإصدار PHP 5.1.

ملاحظة: حتى يقوم الصف الذي عرفته بتحقيق الواجهة Countable يتوجب عليك تعريف طريقة باسم count() تعيد عدد صحيح , ويجب أن يقوم الصف أيضاً بالإشارة بشكل صريح إلى تحقيق الواجهة Countable.

The Iterator interface 

تعتبر العدادات Iterators بمثالة أكثر الأجزاء أهمية في SPL , حيث تمكننا من أستخدام الأغراض ( التي نعرفها )ضمن الحلقات البرمجية loops.

قد تتسائل , لماذا لم تضمن الواجهة Iterator ضمن الواجهة ArrayAccess ؟

يعود السبب في ذلك بسبب عدم توفيرها للآلية مناسبة تخبر فيها PHP عن المفاتيح keys المحتملة وترتيبهم الموافق.

إذا كنت ترغب في الأستفادة من حلقات foreach loop في التعامل مع الأغراض التي قمت بتعريفها , يتوجب عليك القيام بتحقيق الواجهة Iterator. 

حتى تستطيع تحقيق implement الواجهة Iterator , تحتاج إلى كتابة كل من الطرق الخمس التالية:

current()

تعيد هذه الطريقة القيمة الموافقة للدليل الحالي current index’s value

أنت المسؤول الوحيد عن عملية تتبع الدليل الحالي , ولن تقوم الواجهة بتحقيق هذه العملية لك ما لم تعرفها أنت بنفسك.

key()

تقوم هذه الطريقة بإعادة القيمة الموافقةللمفتاح الحالي .

تعتبر هذه الطريقة مهمة جداً بالنسبة لحلقات foreach , حيث يصبح عندها باستطاعتنا الحصول على كل المفاتيح والقيم الحالية  المشار إليها في كل دورة من دورات الحلقة على الغرض الممرر لها.

next()

تقوم هذه الطريقة بتحريك الدليل الداخلي إلى الأمام بمقدار مدخل واحد (إذا نظرنا إلى غرضنا باعتباره عبارة عن سلسلة أو مصفوفة من المداخل المتتالية)

rewind()

تقوم هذه الطريقة بإعادة تهيئة الدليل الداخلي , وتوضيعه ليشير إلى العنصر الأول (المدخل الأول ضمن الغرض).

valid()
تقوم هذه الطريقة بإعادة إحدى القيمتين المنطقيتين true أو false , في حال وجود عنصر في المكان الذي نقف عنده الآن.

تستخدم هذه الطريقة عادة بعد استدعاء إحدا الطريقتين التاليتين rewind() أو next().

تجدر الإشارة إلى أن هنالك عدة أنماط من العدادات iterators التي تقدمها المكتبة النظامية في PHP, ولكننا سنقتصر في مثالنا المطروح على ذكر نمط واحد فقط  ألا وهو  Iterator  ونقوم بتحقيقه implement.

إنشاء وبرمجة أول واجهة خاصة بنا- أي من كودنا الخاص-

حتى تستطيع إنشاء تطبيق محمول portable application , يتوجب عليك في البداية تحديد الآلية التي سيتخاطب عبرها تطبيقك مع قاعدة المعطيات.

في الحل الذي سنطرحه هنا , سنقوم بإنشاء واجهة تؤمن لنا وسيلة تخاطب عامة بالنسبة للتطبيق , أي , مهما كانت قاعدة المعطيات التي تستخدمها , فإن الطرائق المعرفة لدينا ( والهادفة لتحقيق عملية التكامل مع قواعد البيانات المختلفة ) ستقوم بتوحيد واجهة التخاطب دوماً , وبالتالي ستقوم دوماً بأخذ نفس معاملات الدخل (من حيث المفهوم وليس القيمة ), وإعادة نفس القيم.

وكبداية , سنقوم بإنشاء ملف يدعى IDataBaseBindings.php.

ستبدو محتويات هذا الملف مشابهة للكود الموجود أدناه:

 

  

Listing 1 IDataBaseBindings.php
<?php    /**     * IDataBaseBindings interface     *

     * This interface contains the standarized implementation point for all

     * database methods.

     *

     */

 

    interface IDataBaseBindings

    {

        /**

         * getCustomerDetails

         *

         * Returns an UnbufferedAsociativeResultSet containing all

         * detailed information about a user from the userDetails table.

         *

         * @param   int     $userid     A user identification number like 12345

         * @return  UnbufferedAsociativeResultSet    All relevant data from userDetails

         * @throws  ReadOnlyException

         */

        public function getCustomerDetails($userid);

    }

?>

 

 

يقوم الرماز أعلاه بإنشاء واجهة تدعى IDataBaseBindings حيث سيقوم الصف المحقق لها بتعريف الطرائق المذكورة ضمنها .

عبر استخدام واجهة مثل هذه الواجهة , يصبح بإمكانك توفير عدد من الصفوف المختلفة التي تقوم بتحقيق هذه الواجهة , وذلك تبعاً لقاعدة المعطيات المراد استخدامها.

بالنسبة لقواعد معطيات PostgreSQL التي تحقق هذه الواجهة , فإنه بإمكانها استخدام CIDR كنمط معطيات , أما بالنسبة لقواعد معطيات MySQL فإنك ستستخدم القيم البوليانية الرياضية Boolean math من أجل تحديد المعلومات.

كان ذلك مجرد مثال بسيط جداً , ولكن هنالك حالات كثيرة تتطلب منك رفع قدرات قاعدة معطيات معينة لأسباب تتعلق بالإنجاز performance reasons, وبشكل خاص عند التعامل مع الإجرائيات المخزنة stored procedures, أو الإستعلامات المعدة مسبقاً , prepared queries والمعطيات ذات الحجوم الكبيرة .

تحقيق الواجهة IDataBaseBindings التي قمنا بتعريفها سابقاً:

حتى نستطيع القيام بتحقيق الواجهة المعرفة سابقاً , سوف نقوم بإنشاء ملف جديد يدعى PostgreSQL.php. ستبدو محتوياته على الشكل التالي:

 

 

   

Listing 2 PostgreSQL.php

<?php    /**     * PostgreSQL implementer for IDataBaseBindings.     *

     * Implements all members indicated by the interface and

     * optimizes queries for the PostgreSQL database.

     *

     * @author      Kevin McArthur

     * @depends     UnbufferedAssociativeResultSet, IDataBaseBindings

     * @implements  IDataBaseBindings

     */

    class PostgreSQL implements IDataBaseBindings

    {

        public function __construct() {

            // Create Database Connections

        }

 

        /**

         * getCustomerDetails

         *

         * Gets customer data from userDetails specified by userid

         *

         * @see IDatabaseBindings

         */

        public function getCustomerDetails($userid)

        {

            $queryResult = pg_query(‘select * from userDetails where userid = ‘. $userid . ‘;’);

            return new UnbufferedAssociativeResultSet($queryResult);

        }

 

        public function __destruct()

        {

            // Destroy Database Connections

        }

    }

?>

   

يقوم الرماز السابق بإنشاء صف class جديد يدعى PostgreSQL الذي يقوم بدوره بتحقيق implements الطريقة getCustomerDetails() المعرفة سابقاً- بشكل مجرد – ضمن الواجهة IDataBaseBindings التي نقوم بتحقيقها عبر هذا الصف.

يجب أن تكون هذه الطريقة مسؤولة عن القيام بكل الاستعلامات. القيام مباشرةً بوضع واستعادة القيم من قاعدة المعطيات.

إنشاء معمل خاص بقاعدة المعطيات Creating a Database Factory

قد تطرح في نفسك بعض التساؤلات: كيف تكون التعريفات السابقة قد قامت بوضع مفهوم مجرد للواجهة في حال قمنا بإنشاء أغراض جديدة مثل PostgreSQL  بشكل مباشرمنها؟

حتى نحل هذه المشكلة سنقوم باستخدام نموذج المعمل Factory Pattern , والذي يعرف بالشكل التالي:

Listing 3 DatabaseFactory.php
<?php    /**     * DatabaseFactory     *

     * Creates an IDataBaseBindings object for use from the application layer

     *

     * @depends    PostgreSQL, MySQL

     */

    class DatabaseFactory

    {

        /**

         * factory

         *

         * Returns an IDataBaseBindings compatible object based on the type of database

         * defined in $configuration[‘databasetype’]

         */

        public static function factory()

        {

            switch($configuration[‘databasetype’]) {

                case ‘PostgreSQL’:

                    return new PostgreSQL();

                    break;

                case ‘MySQL’:

                    return new MySQL();

                    break;

                default:

                    throw new Exception(‘Database implementer not found’);

            }

        }

    }

?>

 

يقوم الرماز أعلاه بتزويدنا بطريقة نفاذ موحدة تهدف لإنشاء واجهات لأغراض متوافقة ليتم استخدامها ضمن رماز التطبيق.

فعلياً نجد , ومن خلال الرمازالسابق بأن تطبيقك لا يعلم ولا يهتم حول قاعدة المعطيات التي سيتم استخدامها.وإنما فقط يهتم بتلبية المتطلبات المعرفة في الواجهة .

أي باختصار , توحيد واجهة التخاطب البرمجية – مع اختلاف قواعد المعطيات التي من الممكن التعامل معها – قد حل لنا مسألة المحمولية وجعلها متاحة وممكنة لأقصى الدرجات وعبر أكثر الوسائل البرمجية تقدماً (البرمجة غرضية التوجه)

التعامل مع المعطيات المعادة :

UnbufferedAssociativeResultSet 

ربما قد تكون – عبر قراءة الرماز المذكور سابقاً – قد لاحظت بأن الصف PostgreSQL لم يقم بإعادة نتائج الاستعلام بشكل مباشر. السبب في ذلك هو أن مثل هذه العملية (الإعادة المباشرة للنتيجة ) قد تقضي على الهدف المرجو من تعريف الواجهات , لأنه في PHP  لكل قاعدة معطيات طرائق مختلفة في ا لتعامل مع مصادر النتائج result resources التابعة لها .

فعلى سبيل المثال نجد بإن الصف PostgreSQL يستخدم الغرض UnbufferedAssociativeResultSet.

Listing 4 UnbufferedAssociativeResultSet.php 

<?php 
    /**
     * UnbufferedAssociativeResultSet
     *
     * Allows for unified access to a database result set
     *
     * @implements  ArrayAccess, Iterator, Countable
     * @remarks     If not using 5.1, implementing countable can be and count() used directly.
     * @see         SPL Documentation For Interface Declarations.
     */ 
    class UnbufferedAssociativeResultSet implements ArrayAccess, Iterator, Countable 
    { 
        private $currentIndex, $result;
 
        function __construct($result) 
        {
            $this->currentIndex = 0;
            $this->result = $result;
        }
         //Region ArrayAccess
        function offsetExists($offset)
        {
            switch($configuration['databasetype']) {
                case 'PostgreSQL':
                    if(pg_fetch_assoc($this->result, $offset)) {
                        return true;
                    } else {
                        return false;
                    }
                default:
                    throw new Exception("No Database Handler");
            }
        }
 
        function offsetGet($offset)
        {
            switch($configuration['databasetype']) {
                case 'PostgreSQL':
                    if($row = pg_fetch_assoc($this->result, $offset)) {
                        return $row;
                    } else {
                        throw new Exception("No row at ". $offset);
                    }
                default:
                    throw new Exception("No Database Handler");
            }
        }
         function offsetSet($offset,$value)
        {
            throw new Exception("This collection is read only.");
        }
         function offsetUnset($offset)
        {
            throw new Exception("This collection is read only.");
        }
        //EndRegion
         //Region Countable
        function count()
        {
            switch($configuration['databasetype']) {
                case 'PostgreSQL':
                    if($rows = pg_num_rows($this->result)) {
                        return $rows;
                    } else {
                        throw new Exception("Could not fetch the number of rows in resultset");
                    }
                default:
                    throw new Exception("No Database Handler");
            }
        }
        //EndRegion
         //Region Iterator
        function current()
        {
            return $this->offsetGet($this->currentIndex);
        }
         function key()
        {
            return $this->currentIndex;
        }
         function next()
        {
            return $this->currentIndex++;
        }
 
        function rewind()
        {
            $this->currentIndex = 0;
        }
         function valid()
        {
            if($this->offsetExists($this->currentIndex)) {
                return true;
            } else {
                return false;
            }
        }
         function append($value)
        {
            throw new Exception("This collection is read only");
        }
         function getIterator()
        {
            return $this;
        }
        //EndRegion
    }
?>

يقوم الغرض أعلاه بإنشاء مصفوفة من مجموعة النتائج دون أن يتطلب ذلك تحميل المعطيات إلا عند طلبها.

بإمكانك استخدام هذه المصفوفة وكأنها مصفوفة عددية , وكل دليل فيها سيقوم بإعادة المصفوفة المرافقة التي تحوي المفاتيح الموجودة في مجموعة النتائج.

ملاحظة:

1: من أجل الاختصار , قد قمت بإهمال كتابة التعليقات على هذا الصف , واستخدمت استثناءات exceptions  بسيطة.

2:أنت بحاجة لاستخدام الأغراض المشتقة من الصف Exception حتى تستطيع إطلاق استثناءات مثل الاستثناء ReadOnlyException.

يقوم هذا الصف باستخدام switch وذلك من أجل تحديد التوابع المستعملة في قاعدة المعطيات وذلك تبعاً لقاعدة المعطيات التي سيتم التعامل معها.

مثال تطبيقي:

Listing 5 listing-5.php

<?php
    $uid = 12345;
    $db = DatabaseFactory::factory();
    $ubars = $db->getCustomerDetails($uid);
 
    if($ubars[0]['property'] == "Name") {
    echo "The first property was 'name'";
    }
 
    foreach($ubars as $values) {
        foreach($values as $key=>$value) {
            echo $key . '=' . $value;
        }
    }
 
    $rows = count($ubars);
?>

 

الخلاصة :

إن استخدام عدد من مفاهيم البرمجة غرضية التوجه , يمكننا من إنشاء تجريد وتوسعة لقاعدة المعطيات المستخدمة في التطبيق, والذي يمكننا بدوره من استخدام الميزات الأساسية التي تميز قاعدة معطيات عن أخرى – وذلك بحسب الحاجة لها – وبذلك لا نربط انفسنا بقاعدة معطيات معينة , وإنما نربط بقاعدة المعطيات المناسبة أكثر للتطبيق الحالي الذي نقوم ببرمجته , فقاعدة المعطيات أصبحت بالنسبة لنا اشبه بالمتحول الذي نتحكم بالارتباط معه أو لا بحسب حاجتنا له.

وكل ما هو مطلوب منا برمجياً وفق إطار العمل المعرف سابقاً , هو كتابة الرماز المرتبط بالعمليات الخاصة بقاعدة معطيات معينة ليس إلا. وستكون تطبيقاتنا المعدة للانترنت تتميزبكونها قابلة للعمل على منصات التشغيل المختلفة .والتعامل مع قواعد معطيات مختلفة .

 

وبالختام أتمنى لكم الفائدة عبر هذه الترجمة لمقالة بعنوان البرمجة غرضية التوجه المتقدمة في PHP  

لقد استفت كثيرا بعد قرائتها , لذلك ارتأيت ألا تقف الفائدة عندي فقط , لذلك قمت بترجتها وإدراجها في مدونتي عسى ولعل أن ينتفع بها أحدهم.

في حال وجود أي من التساؤلات حول البرمجة غرضية التوجه , أرجو عدم التردد أبداً في طرحها.

وبعد فترة قليلة سأدرج مواضيع أخرى قيمة حول البرمجة غرضية التوجه في PHP   و mysql فقد قرأت منذ فترة قريبة مقالة رهيبة في هذا الصدد , وعلى أساسها وأساس الأفكار التي استوحتها منها فقد قمت بتغير أسلوبي البرمجي بشكل كامل عند التعامل مع قاعدة المعطيات .

وإلى ذلك الحين أسألكم الدعاء و استودعكم الله , والسلام عليكم ورحمة الله وبركاته.

 

المرجع :

http://www.phpriot.com/articles/oop-with-spl-php-5

 

   

 

Advertisements

, , , , ,

  1. أضف تعليق

اترك رد

Please log in using one of these methods to post your comment:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s

%d مدونون معجبون بهذه: