توضح هذه المقالة عدة طرق لإنشاء تطبيقات PHP قابلة للتكوين. تستكشف المقالة أيضًا نقاط التكوين المثالية في التطبيق وتسعى إلى تحقيق التوازن بين كون التطبيق قابلاً للتكوين للغاية ومغلقًا للغاية.
إذا كنت تخطط لجعل تطبيق PHP الخاص بك متاحًا لأشخاص أو شركات أخرى، فأنت بحاجة للتأكد من أن التطبيق قابل للتكوين. كحد أدنى، اسمح للمستخدمين بتعيين تسجيلات الدخول إلى قاعدة البيانات وكلمات المرور بطريقة آمنة بحيث لا يتم نشر المواد الموجودة بها للعامة.
توضح هذه المقالة عدة تقنيات لتخزين إعدادات التكوين وتحرير هذه الإعدادات. بالإضافة إلى ذلك، توفر المقالة أيضًا إرشادات حول العناصر التي يجب جعلها قابلة للتكوين وكيفية تجنب الوقوع في معضلة التكوين الزائد أو الناقص.
التكوين باستخدام ملفات INI
يحتوي PHP على دعم مدمج لملفات التكوين. يتم تحقيق ذلك من خلال آلية ملف التهيئة (INI) مثل ملف php.ini، حيث يتم تحديد الثوابت مثل مهلة اتصال قاعدة البيانات أو كيفية تخزين الجلسات. إذا كنت ترغب في ذلك، يمكنك تخصيص التكوين للتطبيق الخاص بك في ملف php.ini هذا. للتوضيح، قمت بإضافة الأسطر التالية من التعليمات البرمجية إلى ملف php.ini.
myapptempdir=foo
بعد ذلك، قمت بكتابة برنامج PHP صغير لقراءة عنصر التكوين هذا، كما هو موضح في القائمة 1.
القائمة 1. ini1.php
<?php
الدالة get_template_directory()
{
$v = get_cfg_var( "myapptempdir" );
return ( $v == null ) ? "tempdir" : $v;
}
echo( get_template_directory()."n" );
?>
عند تشغيل هذا الكود في سطر الأوامر، تحصل على النتائج التالية:
% php ini1.php
foo
%
رائع . ولكن لماذا لا يمكننا استخدام وظيفة INI القياسية للحصول على قيمة عنصر التكوين myapptempdir؟ لقد قمت ببعض الأبحاث ووجدت أنه في معظم الحالات، لا يمكن الحصول على عناصر التكوين المخصصة باستخدام هذه الطرق. ومع ذلك، يمكن الوصول إليه باستخدام الدالة get_cfg_var.
لجعل هذا الأسلوب أكثر بساطة، قم بتغليف الوصول إلى المتغير في دالة ثانية تأخذ اسم مفتاح التكوين وقيمة افتراضية كمعلمات، كما هو موضح أدناه.
القائمة 2.
دالة ini2.php get_ini_value( $n, $dv )
{
$c = get_cfg_var( $n );
العودة ($c == فارغة)؟
}
الدالة get_template_directory()
{
إرجاع get_ini_value( "myapptempdir", "tempdir" );
}
هذه نظرة عامة جيدة حول كيفية الوصول إلى ملف INI، لذلك إذا كنت تريد استخدام آلية مختلفة أو تخزين ملف INI في مكان آخر، فلن تحتاج إلى تحمل مشكلة تغيير الكثير من الوظائف.
لا أوصي باستخدام ملفات INI لتكوين التطبيق، وذلك لسببين. أولاً، على الرغم من أن هذا يجعل من السهل قراءة ملف INI، إلا أنه يجعل من المستحيل تقريبًا كتابة ملف INI بأمان. لذا فإن هذا مناسب فقط لعناصر التكوين للقراءة فقط. ثانيًا، تتم مشاركة ملف php.ini عبر جميع التطبيقات الموجودة على الخادم، لذلك لا أعتقد أنه يجب كتابة عناصر التكوين الخاصة بالتطبيقات في هذا الملف.
ما الذي تريد معرفته عن ملفات INI؟ الشيء الأكثر أهمية هو كيفية إعادة تعيين مسار التضمين لإضافة عناصر التكوين، كما هو موضح أدناه.
القائمة 3. ini3.php
<?php
echo( ini_get("include_path"."n" );
ini_set("include_path",
ini_get("include_path").":./mylib" );
echo( ini_get("include_path"."n" );
?>
في هذا المثال، أضفت دليل mylib المحلي الخاص بي إلى مسار التضمين، حتى أتمكن من طلب ملفات PHP من هذا الدليل دون إضافة المسار إلى عبارة الطلب.
التكوين في PHP
البديل الشائع لتخزين إدخالات التكوين في ملف INI هو استخدام برنامج PHP بسيط للاحتفاظ بالبيانات. أدناه مثال.
القائمة 4. config.php
<?php
# حدد موقع الدليل المؤقت
#
$TEMPLATE_DIRECTORY = "tempdir";
?>
الكود الذي يستخدم هذا الثابت هو كما يلي.
القائمة 5. php.php
<?php
require_once 'config.php'؛
get_template_directory();
{
$TEMPLATE_DIRECTORY العالمي؛
إرجاع $TEMPLATE_DIRECTORY؛
}
echo( get_template_directory()."n" );
?>
يحتوي الكود أولا على ملف التكوين (config.php)، ومن ثم يمكنك استخدام هذه الثوابت مباشرة.
هناك العديد من المزايا لاستخدام هذه التكنولوجيا. أولاً، إذا قام شخص ما بتصفح ملف config.php، فستكون الصفحة فارغة. لذا يمكنك وضع config.php في نفس الملف كجذر تطبيق الويب الخاص بك. ثانيًا، يمكن تحريره في أي محرر، وبعض المحررين لديهم وظائف تلوين بناء الجملة والتحقق من بناء الجملة.
عيب هذه التقنية هو أنها تقنية للقراءة فقط مثل ملفات INI. يعد استخراج البيانات من هذا الملف أمرًا سهلاً، لكن ضبط البيانات في ملف PHP أمر صعب، بل وفي بعض الحالات مستحيل.
يوضح البديل التالي كيفية كتابة نظام تكوين قابل للقراءة والكتابة بطبيعته.
يعتبر المثالان السابقانللملفات النصية
مناسبين لإدخالات التكوين للقراءة فقط، ولكن ماذا عن معلمات التكوين للقراءة والكتابة؟ أولاً، قم بإلقاء نظرة على ملف التكوين النصي في القائمة 6.
القائمة 6.config.txt
# ملف تكوين التطبيق الخاص بي
العنوان=تطبيقي
TemplateDirectory=tempdir
هذا هو نفس تنسيق الملف مثل ملف INI، لكنني كتبت أداة المساعدة الخاصة بي. للقيام بذلك، قمت بإنشاء فئة التكوين الخاصة بي كما هو موضح أدناه.
القائمة 7. text1.php
<?php
تكوين الطبقة
{
خاص $configFile = 'config.txt'؛
العناصر $ الخاصة = المصفوفة () ؛
دالة __construct() { $this->parse() };
الدالة __get($id) { return $this->items[ $id ] };
تحليل الدالة ()
{
$fh = fopen( $this->configFile, 'r' );
بينما( $l = fgets( $fh ) )
{
إذا ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$this->items[ $found[1] ] = $found[2];
}
}
فكلوز($fh);
}
}
$c = new Configuration()
echo( $c->TemplateDirectory."n");
?>
يقوم هذا الرمز أولاً بإنشاء كائن تكوين. يقوم المنشئ بعد ذلك بقراءة ملف config.txt ويقوم بتعيين عناصر المتغير المحلي مع محتويات الملف الذي تم تحليله.
يبحث البرنامج النصي بعد ذلك عن TemplateDirectory، الذي لم يتم تعريفه مباشرة في الكائن. لذلك، يتم استدعاء الأسلوب __get السحري مع تعيين $id على 'TemplateDirectory'، والذي يقوم بإرجاع القيمة الموجودة في مصفوفة $items لهذا المفتاح.
طريقة __get هذه خاصة ببيئة PHP V5، لذلك يجب تشغيل هذا البرنامج النصي ضمن PHP V5. في الواقع، يجب تشغيل كافة البرامج النصية الموجودة في هذه المقالة ضمن PHP V5.
عند تشغيل هذا البرنامج النصي من سطر الأوامر، سترى النتائج التالية:
% php text1.php
tempdir
%
كل شيء متوقع، يقرأ الكائن ملف config.txt ويحصل على القيمة الصحيحة لعنصر تكوين TemplateDirectory.
ولكن ماذا يجب عليك فعله لتعيين قيمة التكوين؟ من خلال إنشاء طريقة جديدة وبعض أكواد الاختبار الجديدة في هذه الفئة، يمكنك الحصول على هذه الوظيفة، كما هو موضح أدناه.
القائمة 8. text2.php
<?php
تكوين الطبقة
{
...
function __get($id) { return $this->items[ $id ] }
function __set($id,$v) { $this->items[ $id ] = $v };
تحليل الدالة () { ... }
}
$c = التكوين الجديد ()؛
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
?>
الآن، هناك دالة __set، وهي "ابن عم" الدالة __get. لا تحصل هذه الوظيفة على قيمة متغير العضو. يتم استدعاء هذه الوظيفة عند تعيين متغير عضو. يقوم رمز الاختبار الموجود في الأسفل بتعيين القيمة وطباعة القيمة الجديدة.
إليك ما يحدث عند تشغيل هذا الرمز من سطر الأوامر:
% php text2.php
tempdir
com.foobar
٪
جيد جدًا! ولكن كيف يمكنني حفظه في ملف حتى يتم إصلاح التغيير؟ للقيام بذلك، تحتاج إلى كتابة الملف وقراءته. وظيفة جديدة لكتابة الملفات كما هو موضح أدناه.
القائمة 9. text3.php
<?php
تكوين الطبقة
{
...
وظيفة الحفظ ()
{
$nf = '';
$fh = fopen( $this->configFile, 'r' );
بينما( $l = fgets( $fh ) )
{
إذا ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$nf .= $found[1]."=".$this->items[$found[1]]."n";
}
آخر
{
$nf .= $l;
}
}
فكلوز($fh);
نسخ( $this->configFile, $this->configFile.'.bak' );
$fh = fopen( $this->configFile, 'w' );
fwrite( $fh, $nf );
فكلوز($fh);
}
}
$c = التكوين الجديد();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
$c->حفظ();
>
تعمل وظيفة الحفظ الجديدة بذكاء على التعامل مع ملف config.txt. بدلاً من مجرد إعادة كتابة الملف باستخدام عناصر التكوين المحدثة (والتي قد تؤدي إلى إزالة التعليقات)، قرأت الملف وأعدت كتابة محتويات مصفوفة $items بمرونة. بهذه الطريقة، يتم الاحتفاظ بالتعليقات الموجودة في الملف.
قم بتشغيل البرنامج النصي في سطر الأوامر وإخراج محتويات ملف التكوين النصي. يمكنك رؤية الإخراج التالي.
القائمة 10. حفظ مخرجات الوظيفة
%php text3.php
tempdir
com.foobar
% ملف تكوين القطة
# ملف تكوين التطبيق الخاص بي
العنوان=تطبيقي
دليل القالب = foobar
%
يتم الآن تحديث ملف config.txt الأصلي بالقيم الجديدة.
ملفات تكوين XML
على الرغم من سهولة قراءة الملفات النصية وتحريرها، إلا أنها ليست شائعة مثل ملفات XML. بالإضافة إلى ذلك، هناك العديد من المحررين المتاحين لـ XML الذين يفهمون العلامات والهروب من الرموز الخاصة والمزيد. إذًا كيف سيبدو إصدار XML لملف التكوين؟ تعرض القائمة 11 ملف التكوين بتنسيق XML.
القائمة 11. config.xml
<?xml version="1.0"?>
<التكوين>
<العنوان>تطبيقي</العنوان>
<دليل القالب>tempdir</دليل القالب>
</config>
تعرض القائمة 12 نسخة محدثة من فئة التكوين التي تستخدم XML لتحميل إعدادات التكوين.
القائمة 12.xml1.php
<?php
تكوين الطبقة
{
خاص $configFile = 'config.xml';
العناصر $ الخاصة = المصفوفة () ؛
دالة __construct() { $this->parse() };
الدالة __get($id) { return $this->items[ $id ] };
تحليل الدالة ()
{
$doc = new DOMDocument();
$doc->load( $this->configFile);
$cn = $doc->getElementsByTagName( "config");
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach (العقد $ كعقدة $)
$this->items[ $node->nodeName ] = $node->nodeValue;
}
}
$c = التكوين الجديد();
echo( $c->TemplateDirectory."n" );
?>
يبدو أن XML له فائدة أخرى: الكود أبسط وأسهل من الإصدار النصي. لحفظ ملف XML هذا، هناك حاجة إلى إصدار آخر من وظيفة الحفظ، والتي تحفظ النتيجة بتنسيق XML بدلاً من تنسيق النص.
القائمة 13.xml2.php
...
وظيفة حفظ ()
{
$doc = new DOMDocument();
$doc->formatOutput = true
$r = $doc->createElement( "config");
$doc->appendChild( $r );
foreach( $this->items as $k => $v )
{
$kn = $doc->createElement( $k );
$kn->appendChild( $doc->createTextNode( $v ));
$r->appendChild( $kn );
}
نسخ( $this->configFile, $this->configFile.'.bak' );
$doc->save( $this->configFile );
}
...
ينشئ هذا الرمز نموذج كائن مستند XML (DOM) جديدًا ثم يحفظ جميع البيانات الموجودة في مصفوفة $items في هذا النموذج. بعد الانتهاء من ذلك، استخدم طريقة الحفظ لحفظ ملف XML في ملف.
البديل الأخير
لاستخدام قاعدة البيانات
هو استخدام قاعدة البيانات لتخزين قيم عناصر التكوين.الخطوة الأولى هي استخدام مخطط بسيط لتخزين بيانات التكوين. أدناه هو نمط بسيط.
قائمة 14. schema.sql
DROP TABLE إذا كانت هناك إعدادات؛
إنشاء إعدادات الجدول (
المعرف متوسط وليس فارغًا AUTO_INCREMENT،
نص الاسم,
نص القيمة,
المفتاح الأساسي (المعرف)
);
وهذا يتطلب بعض التعديلات بناء على متطلبات التطبيق. على سبيل المثال، إذا كنت تريد تخزين عنصر التكوين لكل مستخدم، فأنت بحاجة إلى إضافة معرف المستخدم كعمود إضافي.
لقراءة البيانات وكتابتها، قمت بكتابة فئة التكوين المحدثة الموضحة في الشكل 15.
القائمة 15. db1.php
<?php
require_once('DB.php' );
$dsn = 'mysql://root:password@localhost/config';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage() }
class Configuration
{
خاص $configFile = 'config.xml';
العناصر $ الخاصة = المصفوفة () ؛
دالة __construct() { $this->parse() };
دالة __get($id) { return $this->items[ $id ] };
الدالة __set($id,$v)
{
عالمي $ ديسيبل؛
$this->items[ $id ] = $v;
$sth1 = $db->prepare( 'حذف من الإعدادات حيث الاسم=?' );
$db->تنفيذ( $sth1, $id );
if (PEAR::isError($db)) { die($db->getMessage());
$sth2 = $db->prepare('INSERT INTO settings ( id, name, value ) VALUES ( 0, ?, ? )' );
$db->execute( $sth2, array( $id, $v ));
if (PEAR::isError($db)) { die($db->getMessage());
}
تحليل الدالة ()
{
عالمي $ ديسيبل.
$doc = new DOMDocument();
$doc->load( $this->configFile);
$cn = $doc->getElementsByTagName( "config");
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach (العقد $ كعقدة $)
$this->items[ $node->nodeName ] = $node->nodeValue;
$res = $db->query( 'SELECT name,value from settings' );
if (PEAR::isError($db)) { die($db->getMessage());
بينما( $res->fetchInto( $row ) ) {
$this->items[ $row[0] ] = $row[1];
}
}
}
$c = التكوين الجديد();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'new foo';
echo( $c->TemplateDirectory."n" );
?>
هذا في الواقع حل مختلط للنص/قاعدة البيانات. يرجى إلقاء نظرة فاحصة على طريقة التحليل. تقرأ هذه الفئة الملف النصي أولاً للحصول على القيمة الأولية، ثم تقرأ قاعدة البيانات لتحديث المفتاح إلى أحدث قيمة. بعد تحديد القيمة، تتم إزالة المفتاح من قاعدة البيانات ويتم إضافة سجل جديد بالقيمة المحدثة.
من المثير للاهتمام أن نرى كيف تعمل فئة التكوين من خلال إصدارات متعددة من هذه المقالة، حيث يمكنها قراءة البيانات من الملفات النصية وXML وقواعد البيانات، كل ذلك مع الحفاظ على نفس الواجهة. أنا أشجعك على استخدام واجهات تتمتع بنفس الاستقرار في تطويرك أيضًا. بالضبط كيف يعمل هذا غير واضح لعميل الكائن. المفتاح هو العقد بين الكائن والعميل.
ما هو التكوين وكيفية تكوينه
قد يكون من الصعب العثور على حل وسط بين عدد كبير جدًا من خيارات التكوين والتكوين غير الكافي. للتأكد، يجب أن يكون أي تكوين لقاعدة البيانات (على سبيل المثال، اسم قاعدة البيانات ومستخدم قاعدة البيانات وكلمة المرور) قابلاً للتكوين. بالإضافة إلى ذلك، لدي بعض عناصر التكوين الأساسية الموصى بها.
في الإعدادات المتقدمة، يجب أن يكون لكل ميزة خيار تمكين/تعطيل منفصل. السماح بهذه الخيارات أو تعطيلها بناءً على أهميتها للتطبيق. على سبيل المثال، في تطبيق منتدى الويب، يتم تمكين ميزة التأخير بشكل افتراضي. ومع ذلك، يتم تعطيل إشعارات البريد الإلكتروني بشكل افتراضي، حيث يبدو أن هذا يتطلب التخصيص.
يجب تعيين جميع خيارات واجهة المستخدم (UI) على موقع واحد. يجب تعيين بنية الواجهة (على سبيل المثال، مواقع القائمة، وعناصر القائمة الإضافية، وعناوين URL التي تربط عناصر محددة بالواجهة، والشعارات المستخدمة، وما إلى ذلك) في موقع واحد. أوصي بشدة بعدم تحديد إدخالات الخط أو اللون أو النمط كعناصر تكوين. يجب أن يتم تعيينها عبر أوراق الأنماط المتتالية (CSS)، ويجب أن يحدد نظام التكوين ملف CSS الذي سيتم استخدامه. تعد CSS طريقة فعالة ومرنة لتعيين الخطوط والأنماط والألوان والمزيد. هناك العديد من أدوات CSS الرائعة، ويجب أن يستفيد تطبيقك من CSS بشكل جيد بدلاً من محاولة وضع المعايير بنفسك.
وفي كل ميزة، أوصي بتعيين من 3 إلى 10 خيارات تكوين. يجب تسمية خيارات التكوين هذه بطريقة ذات معنى. إذا كان من الممكن تعيين خيارات التكوين من خلال واجهة المستخدم، فيجب أن تكون أسماء الخيارات في الملفات النصية وملفات XML وقواعد البيانات مرتبطة مباشرةً بعنوان عنصر الواجهة. بالإضافة إلى ذلك، يجب أن تحتوي جميع هذه الخيارات على قيم افتراضية واضحة.
بشكل عام، يجب أن تكون الخيارات التالية قابلة للتكوين: عناوين البريد الإلكتروني، وCSS الذي سيتم استخدامه، وموقع موارد النظام المشار إليها من الملفات، وأسماء ملفات العناصر الرسومية.
بالنسبة للعناصر الرسومية، قد ترغب في إنشاء نوع ملف تعريف منفصل يسمى السطح، والذي يحتوي على إعدادات ملف التعريف، بما في ذلك وضع ملفات CSS، وموضع الرسومات، وتلك الأنواع من الأشياء. ثم، اسمح للمستخدم بالاختيار من بين ملفات الجلد المتعددة. وهذا يجعل التغييرات واسعة النطاق في شكل ومظهر تطبيقك أمرًا بسيطًا. يوفر هذا أيضًا للمستخدمين الفرصة لتسريح التطبيق بين عمليات تثبيت المنتج المختلفة. لا تغطي هذه المقالة ملفات السطح هذه، لكن الأساسيات التي تتعلمها هنا ستجعل دعم ملفات السطح أسهل بكثير.
تعد قابليةالتكوين
جزءًا حيويًا من أي تطبيق PHP ويجب أن تكون جزءًا أساسيًا من التصميم منذ البداية. آمل أن توفر هذه المقالة بعض المساعدة في تنفيذ بنية التكوين الخاصة بك وتوفر بعض الإرشادات حول خيارات التكوين التي يجب أن تسمح بها.