صفحة 1 من 3 123 الأخيرةالأخيرة
النتائج 1 إلى 15 من 39

الموضوع: بناء مشروع بطريقة البرمجة الكائنية (1)

  1. #1
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613

    بناء مشروع بطريقة البرمجة الكائنية (1)



    بناء مشروع بطريقة البرمجة الكائنية (1)

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

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

    المشروع سيكون عبارة عن برنامج لإدارة المهام بشكل بسيط ، ما أتحدث عنه هو برنامج مشابه لخدمة remember the milk بأبسط صورة ممكنة ، في أول موضوع سنقوم ببناء الأساس لهذا المشروع ، وفي السلاسل القادمة سنقوم باضافة تحديثات على المشروع ونشرح كيفية القيام بذلك ..

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

    أيضاً ستتعلم - ان لم تكن تعلم اصلاً - بعدم اعادة اختراع العجلة ، لاتقم ببرمجة شيء لمجرد البرمجة .. استفد من غيرك ، وعندما نتحدث عن عالم البرامج الحرة فنحن نتحدث عن بحر واسع من المشاريع والاشخاص الذين سبقوك في هذا المجال .. ، انا لا اقول ان تبحث عن script كامل لادارة المهام مثلاًَ وعدم البرمجة بنفسك ، انا اتحدث عن الاشياء الداخلية .. مثلاً : للتعامل مع قاعدة البيانات لديك خياران : ان تقوم ببرمجة class بنفسك يقوم بالتعامل مع القاعدة او ان تعتمد على نظام آخر قام ببرمجته شخص غيرك .. ، الـ captcha ، استخدام الـ javascript ، الـ cache ، نظم القوالب ، الى اخره من الاشياء التي هي عبارة عن اضافات وليس اساس .. ركز على كلمة اساس ، فليس كل مشروع يجب ان يستخدم نظام قوالب مثلاً لكنه يعتبر اضافة مهمة وتسهّل التعامل مع المشروع وتطويره مستقبلاً وهذا هو الهدف من التوجه الى البرمجة الكائنية ..

    للبدء بالمشروع سنقوم بطرح سؤال بسيط .. ، كيف اقوم بإدارة المهام لدي بأبسط طريقة ؟ سؤال بسيط .. الاجابة بالطبع ابسط ، في العالم الحقيقي الواقعي فان ابسط طريقة هي احضار ورقة وقلم وتدوين المهمة ووضع علامة صح بجانبها في حال اكتمالها اليس كذلك ؟ حسناً لنقم بتحويل ذلك الى عالم البرمجة او لغة الحاسب او مهما اردت تسميتها .. عندما نقوم بالتحويل الان فإننا نتحدث عن تصميم قاعدة البيانات في هذا الحالة .. بما أن المشروع يختص بادارة المهام فمن الطبيعي ان يكون هناك جدول للمهام .. اذا سيكون لدينا جدول بإسم tasks ويحتوي على المهام ، وسؤال آخر .. هل هذا المشروع خاص بي وحدي ام للجميع ؟ اجابة هذا السؤال مهمة لتحديد منهج المشروع ، في حالتنا سيكون الجواب " للجميع " اذا من الطبيعي ان يكون هناك جدول آخر للمستخدمين بإسم users ونقوم بالربط بين كل مستخدم ومهماته الخاصه به ..

    قاعدة البيانات ستكون من النوع mysql ، لنستعرض معاً تركيب الجدولين tasks و users ..
    كود:
    users: id(primary), username(unique), password(32), email(unique), real_name, active(0, 1), created(timestamp)
    لدينا المفتاح الاساسي وهو الحقل id والذي يحمل قيم غير متشابهه او فريدة وتتم الكتابة في تلقائياً ، حقل لاسم المستخدم وهو حقل فريد .. حيث انه لا يمكن او لا يسمح بتواجد مستخدمين بنفس الاسم او على الاقل هذا ما اظنه او ما سنقوم باتباعه في هذا المشروع ، وحقل لكلمة سر المستخدم وتكون الكلمة مخزنة بعد تشفيرها عن طريق الـ md5 ، حقل للبريد الالكتروني وهو ايضاً حقل فريد ، الاسم الحقيقي للمستخدم ، هل عضوية المستخدم مفعّلة ام لا .. هذا الحقل يفيد في حالتين تحضرانني الان .. الاولى هي عندما يريد مستخدم ما حذف العضوية الخاصة به فاننا لا نقوم بحذف بيانات هذا العضو بل نقوم بالغاء تفعيل عضويته ونتيح له اماكنية اعادة تفعيل العضوية الخاصة به .. بمعنى انه لايوجد حذف للعضويات بل يوجد تفعيل/الغاء تفعيل للعضويات .. والحالة الاخرى هي في حالة اذا اردنا اضافة خاصية تفعيل العضوية عند التسجيل بواسطة البريد الالكتروني ، الحقل الاخير هو حقل لتاريخ انشاء السجل او العضوية ولا يتم تحديثه .. هذا الحقل ليس فريداً لانه توجد امكانية لان يقوم شخصين بالتسجيل في نفس الوقت والتاريخ .. اليس كذلك ؟
    كود:
    tasks: id(primary), user_id(foreign), title, notes(text), case(completed, uncompleted), created(timestamp)
    المفتاح الاساسي ، المفتاح الاجنبي وهو الحقل الذي يربط المهمة بالمستخدم الذي تندرج تحته ، عنوان او اسم او وصف المهمة ، ملاحظات على المهمة ، حالة المهمة وتكون اما منتهية او غير منتهية ، تاريخ انشاء المهمة وهو غير قابل للتحديث ..

    بالطبع هذه ليست ابسط طريقة لادارة المهام او دعنا نقل انها ليست ببساطة الورقة والقلم ، فمثلاً الحقول الاضافية مثل حقل الملاحظات و التاريخ ليست اساية على عكس الحقول الاخرى .. لكن عندما تتحدث عن البساطة في البرمجة فهذا التركيب للجدول يعد من ابسط التراكيب ..

    فنحن لم نتطرق الى اهمية المهمة ، موقع المهمة ، تاريخ القيام بالمهمة .. وفي حالة المهمة توجد هناك حالتان بينما يمكننا وضع حالات مثل : مؤجلة ، ملغية ، الى آخره ..

    كل هذه الخصائص يمكن اضافتها لكنها ستتطلب وقتاً وجهداً للقيام بذلك ، مثلاً الخدمة المقدمة من remember the milk تتيح لك ربط موقع المهمة بخرائط google ، وتتيح امكانية كتابة المهمة من عن طريق البريد الالكتروني بمعنى ان تقوم بارسال رسالة الى عنوان بريد الكتروني خاص بك وخاص باضافة المهام وتتم اضافة هذه المهمة بكل سهولة ، ايضاً يتيح ارسال رسائل sms للهواتف النقالة للتذكير بالمهمة في موعدها ..

    الان نحن نتطرق لامكانية استخدام المشروع من خارج موقعه وهذا ما يدعى بـ " واجهة البرمجية التطبيقة " او ما يعرف بالـ API ، وان شاء الله سنضيف هذه الخاصية للمشروع مستقبلاً ..

    انا لم اذكر خصائص خدمة الموقع remember the milk كدعاية له ، بل لتبيين الفرق بين بداية مشروع وما سيصبح عليه مع التطوير المستقبلي ، ومن يعلم فربما يأخذ احدهم هذا المشروع ويقوم بتطويره ليصبح مشروعاً متكاملا ..

    نعود الان الى البرمجة ..

    للتعامل مع قاعدة البيانات سنحتاج الى class يبسط لنا التعامل معه ، ما اقصده هو انه بدلاً من تكرار العمليات المعتادة للتعامل مع قواعد البيانات من النوع mysql كـ mysql_query, mysql_fetch_array, mysql_free_result ، وغيرها من الأوامر .. فاننا نقوم بانشاء دالة تقوم بهذه العملية بدلاً من التكرار .. هذا سيكون واضحا لك بعد قليل ان شاء الله ..

    في هذا المشروع التعامل مع قواعد البيانات لن يكون معقداً ، لذلك لن نقوم باستخدام اداة تم برمجتها من قبل ، اعلم الان البعض يرى انه يوجد تناقض بينما قلته سابقاً عن عدم اعادة اختراع العجلة وبينما اقوله الان ، التفسير واضح .. اذا لم تكن بحاجة لاستخدام شيء معقد او يحتوي على مميزات كثيرة انت لست بحاجة لها فلا تقم باستخدامه ، مع انه من الاصح ان اقوم باستخدامها لانه يجعل البرنامج اكثر قابلية للتطوير .. عموماً نحن سنقوم ببرمجة class بسيط جداً يقوم بالتعامل مع قاعدة البيانات .. ، مع ملاحظة انه يمكن استبدال هذا الـ class بآخر يحتوي على مميزات اكثر ، هنا يظهر جلياً احدى مميزات البرمجة الكائنية وهي القابلية لتطوير بطريقة سهلة وفعّالة في نفس الوقت ..

    بكل بساطة فان التعامل مع قاعدة البيانات هو عبارة عن اتصال بالقاعدة ومن ثم تطبيق استعلامات ( querys ) والحصول على نتائج .. حسناً لدينا الاستعلام الذي يجلب مجموعة من البيانات وذلك باستخدام select فنحن سنحتاج للتعامل مع هذه البيانات بطريقة منطقية وارجاعها بشكل واضح ، غالبية الاستعلامات الاخرى ( insert, update, delete ) تعيد احدى قيمتين وهما true في حالة نجاح الاستعلام او false في حالة عدم نجاحه .. وعلى هذا الاساس سنقوم ببناء الـ class ..

    - قد يتسائل البعض عن السبب في كتابتي لكلمة " class " بدلاً من كلاس أو صِنف ، فالسبب في ذلك هو ان البعض قد لا يفهم معنى كلمة " صِنف " ، وانني لا احبذ التعريب الغير دقيق للكلمات الإنجليزية مثل : سكريبت ، كلاس .. لذلك اذكرها باللغة الانجليزية ..

    في البداية قم بتحميل التطبيق والموجود في المرفقات ..

    الان لنقم بإلقاء نظرة على تركيب الـ class الخاص بالتعامل مع قاعدة البيانات بالإسم mysql والموجود في المجلد classes بالاسم mysql.php ..

    تلاحظون وجود متغيرين في الـ class وهما table_name وهو اسم الجدول الذي سيقوم الـ class بالتعامل معه .. والاخر هو db_link وهو مقبض الاتصال بقاعدة البيانات .. ايضاً تلاحظون وجود دوال واضحة التسمية وهي :

    connect : وهي للاتصال بقاعدة البيانات وتحمل قيماً افتراضية يمكن تعديلها ..
    disconnect : لقطع الاتصال بالقاعدة ..
    get: تنفيذ استعلام لاحضار بيانات من قاعدة البيانات وتقوم هذه الدالة بفحص البيانات المعادة وتقوم بتنسيقها واعادتها على شكل مصفوفة .. كما تقوم باعادة الاستعلام وعدد نتائج الاستعلام ..
    _count : حساب نتائج استعلام معين ..
    set: تحديث سجل معين بدلالة معينة ..
    remove: حذف سجل معين بدلالة معينة ..
    query: تنفيذ استعلام معين ويمكن ان يعود بقيمة العنصر المُدخل ( insert id ) ..

    الدوال set, remove, query تعود باحدى القيمتين true او false .. ، وجميع الدوال تعود بـ false او اظهار رسالة في حالة وجود خطأ ما ..

    تلاحظون معي ان هذا الـ class بسيط جداً لكنه يحتوي على ما يكفينا .. اذا كانت لديك اي ملاحظات او اقتراحات على الـ class فلا تتردد بطرحها ..

    ربما يلاحظ البعض انني لم اتوسع في شرح الـ class والسبب في ذلك ان الاوامر معروفة ومكررة ..

    لنستعرض مثالا بسيطاً على هذا الصِنف ( سأبدأ بتسمية الـ class الى " صِنف " من الآن ) ..
    كود PHP:
    $mysql = new mysql;
    $mysql->connect();
    $mysql->table_name 'tasks';
    print_r($mysql->get('select title from tasks'));
    $mysql->disconnect(); 
    النتيجة ستكون ..
    كود:
    Array
    (
        [rows] => Array
            (
                [0] => going to school
                [1] => eating dinner with my friend
                [2] => recording a new twit episode
                [3] => rewriting jquery!
                [4] => watching tv
                [5] => writing a new post in my blog
                [6] => searching in google about "reuse"
            )
    
        [count] => 7
        [query] => select title from tasks
    )
    هذا الصِنف يمكن ان يستخدم على اي جدول تقريباً ..

    إن شاء الله سأكمل لاحقاً ..





    الملفات المرفقة الملفات المرفقة


  2. #2
    عضو نشيط
    تاريخ التسجيل
    Oct 2008
    المشاركات
    81


    الله يعطيك العافية سأكون من المتابعين للموضوع
    وفي إنتظار الجديد






  3. #3
    عضو فعال جدا
    تاريخ التسجيل
    Aug 2007
    المشاركات
    3,929


    شرح جميل , ربنا يجازيك خير علي وقتك
    استفدت جدا من كل كلمة وردت بالدرس





    __________________
    تحاور مع العاقل بعقل .. اما ان يقتنع او تقتنع
    وتحاور مع الجاهل بعقل .. اما يطقعش او تفرقش
    وتلك الفاظ لا معنى لها كذلك كلماته

  4. #4
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613


    حسناً .. لقد قمت ببعض التعديلات على الصِنف mysql ، لقد قمت بإضافة دالتين .. get_value وهي تقوم باحضار نتيجة واحد بدلالة حقل معين .. مثلاً : إطبع عنوان المهمة رقم 1
    كود PHP:
    echo $mysql->get_value('title'3); 
    والدالة الأخرى هي لاختصار بعض الأسطر المكررة ..

    في البداية لنقم بتحديد ما نريده من البرنامج او لنشرح عمل البرنامج ، البرنامج يعطي امكانية لـ :
    - إضافة مستخدم جديد
    - تسجيل دخول المستخدم
    - احضار معلومات المستخدم
    - تغير البريد الإلكتروني ، كلمة السر ، الإسم
    - الغاء تفعيل العضوية
    - اعادة تفعيل العضوية
    - تسجيل خروج المستخدم

    هذا من ناحية المستخدم او العضو وهذه هي الوظائف الاساسية التي سيقوم بها الكائن ( أعتقد أن كلمة كائن أفضل .. ألا تعتقدون ذلك ؟ ) .. وهذا ما سنقوم بتوفيره عن طريق الكائن user الذي يرث الكائن mysql ..
    كود PHP:
    user extends mysql { } 
    من ناحية المهام فإن المشروع يعطي إمكانية :
    - إضافة مهمة جديدة
    - تعديل / حذف مهمة
    - احضار معلومات المهمة
    - تعيين المهمة كمنتهية/غير منتهية

    وايضاً سيكون هناك كائن بإسم tasks ويرث الكائن الرئيسي mysql ..

    حسناً .. لنبدأ بالكائن الخاص بالمستخدم ( user ) ..

    قم بتحميل النسخة الأحدث من المشروع والموجود في المرفقات ..


    بعد إلقاء نظرة سريعة على الكائن الموجود في المجلد classes بإسم user.php يلاحظ انه تم تضمين ملف الكائن الرئيسي mysql ليتم توريث الكائن user له ، في بداية الكائن توجد دالة تعمل تلقائياً عند طلب الكائن .. تقوم هذه الدالة بالإتصال بقاعدة البيانات وتحديد الجدول الذي سيتم التعامل معه وهو users ..

    يلاحظ وجود دالة لإنشاء مستخدم جديد .. تقوم هذه الدالة بإستقبال المعلومات المطلوبة للتسجيل ومن ثم معالجتها ويمكن رؤية ذلك بشكل واضح في هذه الدالة ، يلاحظ ان الدالة لا تتوقف عن العمل ابداً وإنما تعود برقم الخطأ إن وُجد وبالرقم 0 ورقم العضو الجديد في حالة عدم وجود اخطاء ، الأخطاء يتم معرفتها عن طريق مستند بإسم errors-guide.txt ( دليل الأخطاء ) والموجود في المجلد docs ..

    يمكن الملاحظة بوضوح مدى الاختصار والاستفادة التي وفرتها لنا خاصية الوراثة .. حيث انه بسطر واحد تأكدنا من عدم وجود اسم المستخدم من قبل مثلاً ..
    كود PHP:
    if ($this->_count("select id from users where email = 'user@mail-service.com'") != 0) { } 
    اختصرنا خمسة او ستة اسطر تتكرر معنا بشكل كبير بسطر واحد منطقي وواضح ويؤدي المطلوب ..

    يلاحظ وجود خاصية غير مطبقة او تم تجاهلها وهي خاصية التحقق من اسم المستخدم ، وفي حالتنا هذه .. ان يكون اسم المستخدم ذو حروفٍ صغيرة انجليزية وارقام و _ فقط ، وهي عبارة عن مجرد تعبير قياسي بسيط ، فمن لديه القدرة على تنفيذه فليتكرم ويضعه مشكوراً ..

    يلاحظ ايضاً وجود دالتين اضافتين محميتين في اسفل الكائن وهما خاصتان بمعالجة الكائن وبما اننا لن نحتاجهما الى عند التعامل مع المستخدم ، فانني اظن ان هذا هو مكانهما الصحيح ، هل لدى احدكم فكرة او طريقة أخرى لتأدية هذا الغرض ؟

    عند العودة للكائن الرئيسي mysql يلاحظ وجود دالتين اضافيتين غير مرتبطتين بالتعامل مع قاعدة البيانات ، وهما ايضاً للمعالجة وتم وضعهما في هذا الكائن لانه سيتم استخدامها من قبل كائن المستخدم و كائن المهام الذي سنتطرق اليه مستقبلاً او بعد الانتهاء من كائن المستخدم على الأقل ..

    ايضاً في احدى الدوال الإضافية في كائن المستخدم والتي تقوم بمعالجة البريد الالكتروني والتأكد من صحته ، التعبير الخاص بها غير قياسي ولذلك فقد وضعت ملاحظة تفيد بتغييره مستقبلاً ..

    انتهينا الان من وظيفة ادخال المستخدم .. الان لنقم بانشاء الملف الذي سيقوم بانشاء المستخدم او الملف الذي سيتعامل مع الزائر الراغب في التسجيل في هذه الخدمة ، بما أن التعامل مع الزائر يعني اظهار اوامر ونصوص بلغة html فان ذلك يعني انه من الأفضل استخدام نظام قوالب لفصل البرمجة عن التصميم لتسهيل التطوير .. الى اخره من القائمة التي تبين فوائد استخدام نظم القوالب ، يمكننا استخدام نظام قوالب كبير مثل smarty مثلاً لكن ذلك سيكون بلا فائدة لاننا لن نحتاج الى كل خصائصه ، يمكننا برمجة نظام بأنفسنا .. لكن ذلك سيكون مضيعة للوقت لوجود البدائل الجاهزة ، من أفضل نظم القوالب وأبسطها نظام القالب السهل ، وهذا هو النظام الذي سنستخدمه ، مع ملاحظة ان النظام المرفق مع المشروع به بعض التعديلات الطفيفة ، عموماً .. اذا كنت ترغب في استخدام نظام قوالب اخر فالعملية سهلة وميسرة لكن سكون عليك تعديل ملفات القوالب ..

    حسناً .. لنقم الان بإلقاء نظرة على الملف new-user.php والموجود في المجلد الرئيسي .. ، الملف واضح وبسيط جداً .. في حالة وجود ارسال بيانات من المستخدم يتم تنفيذ دالة انشاء المستخدم ، ومن ثم يتم تحويل المتغير الخاص بعرض رسالة تبين وضع العملية الى القيمة 1 لكي يتمكن القالب من التعرف عليها ..

    في مجلد القوالب ( templates ) يوجد هنالك القالب الرئيسي main.tpl وهو خاص بالشكل العام الذي سيكون عليه المشروع بشكل عام ويتم تضمين الصفحات الفرعية فيه ، ملف القالب الخاص بانشاء المستخدم new-user.tpl يقوم بالتحقق من وجود المتغير الخاص باظهار الرسالة ففي حالة كانت قيمته 1 فانها يقوم بالتحقق من رقم الخطأ المعاد من الدالة الخاصة بانشاء المستخدم ومن ثم يظهر الرسالة المرادفة لذلك الرقم ، اما في حالة عدم ارسال بيانات فانه يتم عرض النموذج الخاص بإضافة البيانات ..

    يلاحظ في الملف new-user.php وجود متغيرين يتم اسنادهما تلقائياً - في القالب السهل - الى القالب ، وهما title وهو عنوان الصفحة ، و content وهو محتوى الصفحة .. نقوم باسناد قيمة ما الى العنوان ونقوم باسناد قيمة ملف القالب new-user.tpl الى المتغير الخاص بالمحتوى .. ، ومن ثم يتم عرض الناتج النهائي ..

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

    من التطويرات المستقبلية المقترحة : إضافة الـ captcha او صورة التحقق لمنع الـ spam وهذه سيتم اضافتها مستقبلاً ، نحن الآن نركز على الاساس ومن ثم نتطرق للاضافات ..

    نُكمل لاحقاً ..





    الملفات المرفقة الملفات المرفقة

  5. #5
    عضو نشيط
    تاريخ التسجيل
    Oct 2008
    المشاركات
    81


    يلاحظ وجود خاصية غير مطبقة او تم تجاهلها وهي خاصية التحقق من اسم المستخدم ، وفي حالتنا هذه .. ان يكون اسم المستخدم ذو حروفٍ صغيرة انجليزية وارقام و _ فقط ، وهي عبارة عن مجرد تعبير قياسي بسيط ، فمن لديه القدرة على تنفيذه فليتكرم ويضعه مشكوراً ..
    تفضل :
    لاحظتها الموجودة في user.php عند
    كود PHP:
    // username must be in english only and without any special characters
      /*} elseif () {
       $error_number = 2;*/ 
    هذا بعد كتابة التعبير :
    كود PHP:
    } elseif (!ereg('^[a-z0-9_]+$'$username)) {
       
    $error_number 2
    مثل ما طلبت أحرف إنكليزية صغير فقط و أرقام و _
    أتمنى تكون هية ما تريد و تكون صحيحة 100%






  6. #6
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613


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

    في البداية سنحتاج لتحميل النسخة الأحدث من المشروع والموجودة في المرفقات ..

    نبدأ بالدالة الخاصة بتسجيل الدخول والموجودة في الملف user.php في المجلد classes ..

    الدالة تستقبل ثلاث عناصر ، إسم المستخدم ( الـ username ) و كلمة السر الخاصة بالمستخدم ، وعنصر اختياري وهو " تذكرني " .. في البداية نقوم بمعالجة العنصرين الأولين وذلك ، ونقصد بالمعالجة جعلها صالحة للتعامل مع قاعدة البيانات لكي نضمن عدم وجود اي ثغرات يمكن استغلالها بشكل سيء ، ثم نقوم بالتأكد من عدم خلو المتغيرين اي انه تم ادخال قيمة في كلاً منهما ، في حالة عدم خلوهما سيقوم البرنامج بالتأكد من صحة هذه المعلومات .. إستعلام بسيط يؤدي الغرض
    كود PHP:
    $this->_count('select id from users where username = \''.$username.'\' and password = \''.$password.'\''
    في حالة صحة هذه المعلومات سيقوم البرنامج بنخزين كعكة او cookie في جهاز المستخدم ، اذا تم تفعيل خيار التذكّر فسيتم تخزين الكعكة لمدة اسبوعين من تاريخ تسجيل الدخول في جهاز الزائر ( أظن أنها الفترة الاكثر ملائمة لتخزين معلومات المستخدم ) ، اما اذا لم يتم تفعيله فان مانقوم به هو عبارة تخزين مؤقت حتى يتم اغلاق المتصفح من قبل الزائر ..

    السؤال هنا .. ما هو الحل في حالة عدم سماح المستخدم بالـ cookies ؟ الجواب هو الـ sessions .. لكن بذلك ستكون العملية معقدة نوعاً ما .. سنحاول اضافة هذه الخاصية في تطويرات قادمة ..

    نأتي الان للطريقة او الكيفية التي سيتم بها التخزين ، فلا يعقل ان نقوم بتخزين كلمة السر الغير مشفرة مثلاً ! يجب ان نضع حماية قصوى حول هذه المعلومات ، الكعكة ستكون عبارة عن مصفوفة تحوي اسم المستخدم الغير مشفر وكلمة السر المشفرة مضافة الى متغير جديد وهو اسم الكعكة بمعنى ان كلمة السر بشكلها في قاعدة البيانات لايمكن ان تخرج منها أبداً .. ضع ذلك في بالك قبل برمجة اي نظام مثل هذا ..
    كود PHP:
    md5($password.$this->c_name
    اذا ستكون الكعكة عبارة عن مصفوفة تحتوي على اسم المستخدم و كلمة السر المشفرة او المُضلِّلة ، على حد علمي انه لايمكن تخزين المصفوفات في الكعكات لذلك سنقوم تمرير المصفوفة على الدالة serialize ومن ثم الدالة base64_encode وهي ثنائية الاتجاه .. الان ستكون معلومات المستخدم مخزنة بالطريقة
    كود PHP:
    base64_encode(serialize(array(md5($this->c_name.'u') => $usernamemd5($this->c_name.'p') => md5($password.$this->c_name)))) 
    بقيت نقطة أخيرة وهي في حالة تم تسجيل الدخول بنجاح اي ان رقم الخطأ الذي ستعيده الدالة = 0 .. يتم تخزين رقم المستخدم ( الـ id الخاص به ) في متغير مؤقت يكون متوفراً وقت تنفيذ الكائن ، السبب في ذلك هو اننا في حالة اردنا احضار معلومات المستخدم او في دوال اخرى كما سنرى لاحقاً نقوم بطلبها بدون تمرير اي parameters ( أرجو انني كتبتها بشكل صحيح ) اي نطلبها بالشكل
    كود PHP:
    $user->user_info(); 
    وعلى الكائن معرفة رقم المستخدم بنفسه ، وافضل وسيلة لذلك هي بتخزين رقم المستخدم في هذا المتغير ..

    بالطبع الدالة تقوم بمعالجة الاخطاء فهي لا تقوم بارجاع ( true or false ) بل تقوم بارجاع رقم الخطأ وبالطبع ارقام الاخطاء لهذه الدالة متوفرة في الملف errors-guide.txt في المجلد docs ..

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

    بالمناسبة اذا كانت لديكم اي اضافات على طريقة تخزين الكعكة في جهاز المستخدم فلا تترددو في طرحها ..

    حسناً .. ننتقل الآن الى الدالة الخاصة بالتحقق من حالة المستخدم هل قام بتسجيل الدخول ام لا .. الدالة لا تستقبل اي عناصر بل يتم تنفيذها مباشرة .. بشكل طبيعي تقوم الدالة بالتحقق من وجود الكعكة التي تحتوي على معلومات المستخدم ، ومن ثم تتحقق من كون هذه المعلومات عبارة عن مصفوفة ، ومن ثم يتم فك طريقة التشفير او التخزين بالطريقة
    كود PHP:
    unserialize(base64_decode($_COOKIE[$this->c_name])) 
    ومن ثم يتم معالجة اسم المستخدم واحضار كلمة السر الصحيحة لهذا المستخدم ومن ثم مقارنة النسخة المعدلة منها مع القيمة الموجودة في الكعكة .. الدالة تعود بقيمة true في حالة كون المستخدم مسجل الدخول بطريقة صحيحة ، و flase في حالة العكس ..

    قد يتسائل البعض عن سبب التعقيد او التدقيق في معلومات الكعكة ، السبب في ذلك هو انه لايمكن ضمان اي معلومات من خارج البرنامج من دون معالجة .. ولذلك قمنا بتشفير اسم الكعكة واسماء اجزاء الكعكة .. نظرة على الشفرة المصدرية ويمكنك ملاحظة ذلك ..

    ننتقل الان للدالة الخاصة بتسجيل الخروج .. لا تستقبل اي عناصر ، تتأكد من ان المستخدم قام بتسجيل الدخول ومن ثم تقوم بحذف الكعكة او مسح القيمة في هذه الكعكة ، هل لدى احدكم طريقة أخرى للحذف ؟ الدالة تعود بـ true او false

    نقطة اخرى وهي انني قمت بحذف الدالة is_match لانه لايوجد داعي لاستخدامها ، فالمعامل == يفي بالغرض ..

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

    اذا لاحظ احدكم اي اخطاء في الشفرة او حتى اخطاء منطقية فلا يتردد في طرحها هنا ..

    ايضاً اود معرفة آرائكم في طريقة سير الموضوع .. هل هي مناسبة ، هل فكرة المشروع مناسبة ؟ هل الكائن mysql مناسب ؟ الى اخره من هذا النوع من الاسئلة ..





    الملفات المرفقة الملفات المرفقة

  7. #7
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613


    الشكر موصول للأخ " سعد السيد احمد " لمشاركته في المشروع .. ، الحقيقة السبب في وضعي لذلك السؤال هو ايجاد نوع من النقاش والتفاعل ، شكراً لك مرة أخرى






  8. #8
    عضو نشيط جدا
    تاريخ التسجيل
    Sep 2003
    المشاركات
    538


    مشكور الاخ سعد السيد احمد علي هذه المشاركة الرائعة






  9. #9
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613


    هذا الجزء سيكون مختصراً نوعاً ما ، لأنه يتحدث عن أشياء عامة وتغييرات لا جديد فيها .. أقصد من حيث النوع ..

    هذا الملف يحتوي على آخر نسخة متوفرة من المشروع والموجودة في المرفقات ..

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

    هناك تعديل بسيط على دالة تسجيل الدخول حيث اصبحت تقبل التأكد من تسجيل مستخدم بعينه ..
    كود PHP:
    $user->login('test'123456); 
    ايضاً التحديثات في ملف تعريف الأخطاء في المجلد docs متوفرة ..

    البداية ستكون بالدالة الخاصة بعرض معلومات المستخدم ، الدالة لاتحتاج الا لتعريف متغير او ثابت واحد وهو رقم المستخدم ( الـ id ) ويتم تعريفه عن طريق التحقق من الدخول بواسطة الدالة login_check() ، وتعيد معلومات المستخدم ( الاسم ، تاريخ التسجيل ، البريد الإلكتروني ، الخ .. ) ولكنها لا تعيد كلمة السر لانها تندرج تحت بند " سري جداً " .. الدالة تعود بقيمة null في حالة عدم تعريف الـ id الخاص بالمستخدم ، هناك نقطة خطرت في بالي وهي اذا كان الـ id الخاص بالمستخدم غير موجود .. ولكن ذلك أمر مستبعد فلذلك تم تجاهل هذا الأمر ..

    الدوال الثلاثة الأخرى خاصة بتحديث او تغيير بيانات المستخدم ( كلمة السر ، اسم المستخدم الحقيقي ، البريد الإلكتروني ) ، الدوال تقوم بالتأكد من صحة البيانات المدخلة واذا كانت صحيحة يتم التحديث عن طريق الدالة set() ، وبالطبع الدوال تعود برقم الخطأ الذي صادف العملية او 0 اذا لم يكن هناك اية اخطاء ، انظر الملف test.php في المجلد الرئيسي لامثلة على كيفية استخدام هذه الدوال الثلاثة ..

    القيم سيتم أخذها من الرابط في المثال ..
    كود:
    test.php?a=123456&b=abc123&c=abc123
    test.php?a=test@test.test&b=test@test.test
    test.php?a=nasser
    هذا في المثال فقط ، اما في التطبيق النهائي فسنتعامل مع forms بالطريقة post ..

    بهذا نكون قد انتهينا من البنية الاساسية للكائن user ، وبقي علينا انشاء الملفات التي ستستخدم هذا الكائن ، كنا قد أنشئنا ملف انشاء المستخدم الجديد مسبقاً ، يمكنك الاطلاع عليه في آخر اصدار من المشروع ..

    لمن توجد لديه اي فكرة او خاصية يقترح اضافتها في المشروع فليتفضل بها ..

    في الجزء القادم سنناقش الكائن task او قد نقوم بانشاء الملفات التي ستتعامل مع الكائن الخاص بالمستخدم ..

    نُتابعُ لاحقاً ..

    - تحديث : تم إرفاق الملف login.php الذي سقط سهواً من ملفات المشروع السابقة





    الملفات المرفقة الملفات المرفقة
    التعديل الأخير تم بواسطة dev11 ; 04-11-2008 الساعة 05:42 PM

  10. #10
    عضو نشيط
    تاريخ التسجيل
    Jul 2008
    المشاركات
    128


    في
    users.php
    يجب تغيير كما في



    $email_again = $this->_parse($email_again);
    $real_name = $this->_parse($real_name);
    if ($this->is_empty(array($username, $password, $password_again, $email, $email_again, $real_name)) == true) {
    $error_number = 1;
    } elseif (ereg('[a-z0-9_]+', $username) == false) {
    $error_number = 2;
    } elseif (strlen($username) < 3) {
    $error_number = 3;
    } elseif ($this->_count("select id from users where username = '$username'") == 1) {
    $error_number = 4;
    } elseif ($password != $password_again) {
    $error_number = 5;
    } elseif (strlen($password) < 6) {
    $error_number = 6;
    } elseif ($email != $email_again) {
    $error_number = 7;
    } elseif ($this->is_email($email) == false or $this->is_email($email_again) == false) {
    $error_number = 8;
    } elseif ($this->_count("select id from users where email = '$email'") == 1) {
    $error_number = 9;
    } else {
    $result = $this->query("insert into users ( username, password, real_name ,email , active ) values ( '$username', md5('$password'), '$real_name', '$email', 1 )", true);


    النقص في

    $email_again = $this->_parse($email_again);



    و ايضأ في التعامل مع قاعدةالبيانات
    $real_name', '$email', 1
    صحح معي فأنا لست خبيرأ



    وشكرأ على جهودكم






  11. #11
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613


    نعم بالفعل .. تم تحديث الملف





    الملفات المرفقة الملفات المرفقة

  12. #12


    قمت بقراءة الدرس الأول والثاني
    ومازلت اقرأ ولكن هذه الأسئلة كبداية

    لما قمت بعمل دالة الحماية
    كود PHP:
    return mysql_real_escape_string(htmlspecialchars(trim(stripslashes($string)))); 
    أليست هذه كافية
    كود PHP:
    mysql_real_escape_string(trim($string)) 
    أيضا إستعلام الحصول علي عدد الحقول
    مثلا تريد عدد الاعضاء في جدول البيانات
    لو قمت بطريقتك ففيها يتم تحميل كللللللل البيانات عن الأعضاء كلهم في مصفوفة في الرام
    ثم عدهم
    ثم تحرير الرام

    كود PHP:
    mysql_free_result($result); 
    والعودة بالرقم

    طيب ما رأيك في هذه التعليمة

    كود PHP:
    SELECT COUNT(idFROM `usersWHERE `email` = '$email' 

    يلاحظ ايضاً وجود دالتين اضافتين محميتين في اسفل الكائن وهما خاصتان بمعالجة الكائن وبما اننا لن نحتاجهما الى عند التعامل مع المستخدم ، فانني اظن ان هذا هو مكانهما الصحيح ، هل لدى احدكم فكرة او طريقة أخرى لتأدية هذا الغرض ؟
    وأيضا هناك الدالة

    كود PHP:
    protected function _parse($string) {
            return 
    mysql_real_escape_string(htmlspecialchars(trim(stripslashes($string))));
        } 
    ليس لها دخل بال mysql

    فما رأيك بعمل كلاس جديدة تسمي

    global_functions في ملف خاص وليكن بإسم global_functions.php
    ويتم وضع أية دوال عامة لا تنتمي لكائن محدد مثل تلك الثلاثة دوال ؟

    من التطويرات المستقبلية المقترحة : إضافة الـ captcha او صورة التحقق لمنع الـ spam وهذه سيتم اضافتها مستقبلاً ، نحن الآن نركز على الاساس ومن ثم نتطرق للاضافات ..

    أكبر شيئ تسبب في فشل الكثير من المشاريع البرمجية أو الشرح هو إرادة اصحابها من أول إصدارة عمل كل ما يريدوه فيها
    فيأتي لملف اليوزر مثلا وكل ما يراه في البرمجيات الكبيرة يحاول تحقيقه
    ينتهي بملف اليوزر بعد شهر :eek2:
    فيمل ويشعر انه لم يفعل شيئ فشهر كامل ولم يقم سوي بملف واحد
    فيتوقف العمل

    لهذا فعلي طريقتك الحالية أنت تجنب الخطر ذلك

    موفق في كل شيئ

    بارك الله فيك
    لي عودة لقراءة باقي الدروس بإذن الله فقد أستفدت منك في تعليمة لم أكن أعرفها وهي
    كود PHP:
    mysql_insert_id() 
    مما يبشر بالكثير





    __________________
    السيف أصدق أنباء من الكتب

  13. #13
    عضو سوبر نشيط
    تاريخ التسجيل
    May 2007
    المشاركات
    613


    حسناً .. الطريقة الثانية تكفي لحماية المدخلات .. لكن نحن لانريد تفعيل الـ html في اغلب حقول قاعدة بيانات المشروع وذلك سبب إستخدامنا للدالة htmlspecialchars أم بالنسبة للدالة stripslashes فهي تعمل اذا كانت خاصية الـ magic quotes مفعلة حيث ان الدالة mysql_real_escape_string تقوم بهذا العمل ايضاً ، يمكن الاستغناء عن هذا الدالة في حالة عدم تفعيل الخاصية سابقة الذكر ، يمكن التعمق بشكل أكبر في الدالة مستقبلاً لتصبح مثل
    كود PHP:
    function _parse($string$html false) {
        if (
    get_magic_quotes_gpc() == true) {
            
    $string stripslashes($string);
        }
        if (
    $html == false) {
            
    $string htmlspecialchars($string);
        }
        return 
    mysql_real_escape_string(trim($string));

    في الواقع يمكن الاستغناء بشكل كامل عن الدالة stripslashes في هذه العملية .. ، اما عن ارتباط هذه الدالة بالكائن mysql يوجد ارتباط بالفعل حيث اننا نقوم بمعالجة البيانات لتصبح صالحة للاستخدام في mysql ولانحتاج لهذه الدالة في الكائن user او task مثلاً .. بالنسبة للدالة _count فأنت معك حق في هذه النقطة ، لكن عندما نريد الاستعلام بالطريقة
    كود:
    SELECT COUNT(id) FROM `users` WHERE `email` = 'test@test.test'
    فاننا نقوم باستخدام الدالة get
    كود PHP:
    $mysql->get('SELECT COUNT(id) FROM `users` WHERE `email` = "test@test.test"'); 
    وستعود بالقيمة المطلوبة ، ولاحظ ان الدالة لا تقوم باحضار جميع البيانات بل تقوم باحضار الـ id فقط
    كود:
    .. count(id) ..
    يمكن تحويل الدالة _count لتتعامل مع الطريقة count(id) فقط ولكن ذلك سيحتاج الى التعامل مع التعابير القياسية لتحليل الاستعلام المطلوب ومن ثم ايجاد الاستعلام المطلوب ، ربما يتم عمل هذه النقطة مستقبلاً .. لاحظ معي ان الدالة _count تستقبل الاستعلام فقط وتحسب النتائج مباشرة ، لكن عندما نريد استخدام الطريقة count(id) فاننا سنحتاج الى التعامل مع التعابير القياسية كما ذكرت قبل قليل ..

    بالنسبة لانشاء الكائن _global فهذه فكرة عملية بالفعل ، حيث انه يصبح الكائن الرئيسي ، ولايحتوي فقط على الدوال الرئيسية بل يحتوي على المتغيرات الرئيسية ايضاً مثل متغيرات قاعدة البيانات وغيرها ، والكائن mysql يرث هذا الكائن وبهذا الشكل تكون الدوال قابلة للاستخدام في الكائنين user و task ..
    كود PHP:
    class _global { }
        class 
    mysql extends _global { }
            class 
    user extends mysql { }
            class 
    task extends mysql { } 
    بالنسبة للتعليمة mysql_insert_id() فهي تقوم باحضار رقم العنصر المدخل الى القاعدة ، ويكون ذلك بعد الاستعلام مباشرة ، هذه التعليمة مقيدة جداً حيث انها تختصر عدة سطور للحصول على نفس النتيجة ..

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





    التعديل الأخير تم بواسطة dev11 ; 07-11-2008 الساعة 03:52 PM

  14. #14


    حسناً .. الطريقة الثانية تكفي لحماية المدخلات .. لكن نحن لانريد تفعيل الـ html في اغلب حقول قاعدة بيانات المشروع وذلك سبب إستخدامنا للدالة htmlspecialchars أم بالنسبة للدالة stripslashes فهي تعمل اذا كانت خاصية الـ magic quotes مفعلة حيث ان الدالة mysql_real_escape_string تقوم بهذا العمل ايضاً ، يمكن الاستغناء عن هذا الدالة في حالة عدم تفعيل الخاصية سابقة الذكر ، يمكن التعمق بشكل أكبر في الدالة مستقبلاً لتصبح مثل
    كود PHP:
    function _parse($string$html false) {
        if (
    get_magic_quotes_gpc() == true) {
            
    $string stripslashes($string);
        }
        if (
    $html == false) {
            
    $string htmlspecialchars($string);
        }
        return 
    mysql_real_escape_string(trim($string));

    في الواقع يمكن الاستغناء بشكل كامل عن الدالة stripslashes في هذه العملية .. ، اما عن ارتباط هذه الدالة بالكائن mysql يوجد ارتباط بالفعل حيث اننا نقوم بمعالجة البيانات لتصبح صالحة للاستخدام في mysql ولانحتاج لهذه الدالة في الكائن user او task مثلاً .. بالنسبة للدالة _count فأنت معك حق في هذه النقطة ، لكن عندما نريد الاستعلام بالطريقة
    كود:
    SELECT COUNT(id) FROM `users` WHERE `email` = 'test@test.test'
    فاننا نقوم باستخدام الدالة get
    كود PHP:
    $mysql->get('SELECT COUNT(id) FROM `users` WHERE `email` = "test@test.test"'); 
    وستعود بالقيمة المطلوبة ، ولاحظ ان الدالة لا تقوم باحضار جميع البيانات بل تقوم باحضار الـ id فقط
    كود:
    .. count(id) ..
    يمكن تحويل الدالة _count لتتعامل مع الطريقة count(id) فقط ولكن ذلك سيحتاج الى التعامل مع التعابير القياسية لتحليل الاستعلام المطلوب ومن ثم ايجاد الاستعلام المطلوب ، ربما يتم عمل هذه النقطة مستقبلاً .. لاحظ معي ان الدالة _count تستقبل الاستعلام فقط وتحسب النتائج مباشرة ، لكن عندما نريد استخدام الطريقة count(id) فاننا سنحتاج الى التعامل مع التعابير القياسية كما ذكرت قبل قليل ..

    بالنسبة لانشاء الكائن _global فهذه فكرة عملية بالفعل ، حيث انه يصبح الكائن الرئيسي ، ولايحتوي فقط على الدوال الرئيسية بل يحتوي على المتغيرات الرئيسية ايضاً مثل متغيرات قاعدة البيانات وغيرها ، والكائن mysql يرث هذا الكائن وبهذا الشكل تكون الدوال قابلة للاستخدام في الكائنين user و task ..
    كود PHP:
    class _global { }
        class 
    mysql extends _global { }
            class 
    user extends mysql { }
            class 
    task extends mysql { } 
    بالنسبة للتعليمة mysql_insert_id() فهي تقوم باحضار رقم العنصر المدخل الى القاعدة ، ويكون ذلك بعد الاستعلام مباشرة ، هذه التعليمة مقيدة جداً حيث انها تختصر عدة سطور للحصول على نفس النتيجة ..

    اتوجه لك بالشكر الجزيل على فتح باب للنقاش في الموضوع .. في الواقع هذا هو هدفي الاساسي من طرح هذا مثل هذا الموضوع .. وجود نقاط معينة يتم النقاش فيها وتفيد الكثير من الاشخاص المهتمين بمثل هذه المواضيع .. شكراً لك مرةً أخرى ..
    بالنسبة لحماية المدخلات يمكنك جعلها فقط mysql_real_escape_string دائما لو كان البيان لا يحوي هتمل
    ولو كان يحويها نستخدم أي شئ للهتمل فلا داعي لكل تلك التعقيدات أم كل get و post

    أي أننا في كل حالة get أو post سنقوم بالتفكير في أسلم طريقة
    قد تكون أسلم طريقة لنا هي intval فقط وقد تكون mysql_real_escape_string فقط إلخ

    أما بالنسبة لل count يمكنك جعلها هكذا count(*)
    فستعود بالكل :nice:

    أما بالنسبة ل
    mysql_insert_id()

    فلا داعي لتوضح لي كيف توفر :shy:
    صاحبك كان يتدبس وبعد الإدخال يعمل عملية select بالإسم مرة أخري ليحصل علي الرقم :eek3:

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

    لهذا أحاول جزء من وقتي مثلكم للمشاركة ولو بالقليل فربما أكون سبب في تعليم غيري





    __________________
    السيف أصدق أنباء من الكتب

  15. #15
    عضو نشيط جدا
    تاريخ التسجيل
    Jan 2008
    المشاركات
    444





ضوابط المشاركة

  • لا تستطيع إضافة مواضيع جديدة
  • لا تستطيع الرد على المواضيع
  • لا تستطيع إرفاق ملفات
  • لا تستطيع تعديل مشاركاتك
  •  

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