لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 07/30/25 في كل الموقع
-
1 نقطة
-
لتصبح مبرمج جيد ستحتاج إلى التعمق أكثر بجانب الدرس والمشاهدة والقراءة، ولكن ليس بشكل متعمق كثيرًا فتنجرف وتضيع الوقت، أي خير الأمور الوسط، ثم بعد ذلك لو احتجت أمر ما تستطيع البحث عنه ودراسته. والتركيز على المفاهيم المؤثرة بنسبة كبيرة والتي ستسخدمها بنسبة كبيرة في المشاريع، وليس التركيز على الأمور الدقيقة التي تستخدمها بشكل قليل أو نادر. أي إتباع قاعدة 80/20. والفكرة ليس في التطبيق على كل 15 دقيقة، بل التطبيق على مفهوم معين تعلمته، مثلاً تعلمت أساسيات حلقات التكرار في بايثون، هنا تتوقف وتقوم بتنفيذ تمرين للتطبيق على ذلك المفهوم، وربط التمرين بما تعلمته سابقًا أي القوائم والمتغيرات والجمل الشرطية وهكذا. في حال كان الدرس طويل أو به مفاهيم مختلفة، أرجو قراءة التالي، ستجد به توضيح لما سبق:1 نقطة
-
ذلك متاح من خلال منصة GitHub، اضغطي على المجلد، وسيتم عرض محتواه، ستجدي أعلى اليمين ثلاث نقاط ... اضغطي عليهم ثم اختاري delete directory. ثم اضغطي على commit changes. لكن الطريقة الصحيحة هي حذف المجلد على حاسوبك في المستودع المحلي ثم دفع التغييرات إلى GitHub من خلال الأوامر: git add . git commit -m "deleted GitTutorProject/img" git push -u origin main وسيتم تحديث المستودع البعيد على github وحذف المجلد. ولتجنب إدخال نفسك في متاهة مفرغة، أرجو إنشاء مجلد للدورة ثم بداخله قم بإنشاء عدّة مجلدات حيث مجلد لكل مسار في الدورة. وبداخل كل مجلد قم بوضع التطبيقات العملية التي قمنا بها في ذلك المسار. ثم رفع المجلد الرئيسي بالكامل بما يحتويه من مجلدات على مستودع GitHub وتوفير الرابط الخاص به عند التقدم للإختبار. وفي حال وجود مشروع به الكثير من الملفات والمجلدات ويحتاج إلى مجلد خاص به مثل مشروع "تخصيص نموذج لغة باستخدام LangChain و OpenAI" هنا تقوم برفع المشروع على مستودع GitHub منفصل خاص به.1 نقطة
-
لقد انشأت متجر الكتروني لكن في صفحة details في شاشة هاتف لقد رأيت سكرول من محور الاكس وانا متاكد ان لا يوجد شيء خاررج صفحة هل يمكنكم مساعدتي https://arabic-store.netlify.app/ store.zip1 نقطة
-
المصفوفة (array) هي بنية بيانات تخزّن عدة قيم في قيمة واحدة، وهي خريطة مرتبة تربط بين القيم والمفاتيح. تُنشَأ مصفوفة مباشرة عبر الأقواس المعقوفة [] أو عبر الدالة Array() التي تأخذ المعاملات التالية: المعامل التفاصيل key (المفتاح) المفتاح هو المعرّف الفريد وفهرس المصفوفة. من الممكن أن يكون سلسلة نصية أو عدد صحيح مثل 'foo'، '5'، 10، 'a2b' value (القيمة) يوجد لكل مفتاح قيمة مقابلة (وإلا تكون null ويمكن أن تكون القيمة من أي نوع، وستظهر رسالة خطأ إذا حاولت الوصول إليه) table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } تهيئة مصفوفة يمكن أن نُهيأ مصفوفة فارغة عبر الدالة Array() أو الأقواس [] مباشرةً: // مصفوفة فارغة $foo = array(); // PHP 5.4 يمكن استخدام هذا التدوين المختزل بدءًا من الإصدار $foo = []; ويمكن أن نُهيأ مصفوفة ونعطيها قيم في نفس الوقت: // إنشاء مصفوفة بسيطة من 3 سلاسل نصية $fruit = array('apples', 'pears', 'oranges'); // PHP 5.4 يمكن استخدام هذا التدوين المختزل بدءًا من الإصدار $fruit = ['apples', 'pears', 'oranges']; ويمكن أيضًا أن نهيأ مصفوفة مع فهارس مخصصة (تسمى مصفوفة ترابطية associative array): // مصفوفة ترابطية بسيطة $fruit = array( 'first' => 'apples', 'second' => 'pears', 'third' => 'oranges' ); // يمكن أن نضبط القيمة والمفتاح كالتالي $fruit['first'] = 'apples'; // PHP 5.4 يمكن استخدام هذا التدوين المختزل بدءًا من الإصدار $fruit = [ 'first' => 'apples', 'second' => 'pears', 'third' => 'oranges' ]; ستنشئ PHP المتغير تلقائيًا إذا لم يكن قد اُستخدم سابقًا، وهذا يجعل الشيفرة ملائمة لكنها صعبة القراءة: $foo[] = 1; // Array( [0] => 1 ) $bar[][] = 2; // Array( [0] => Array( [0] => 2 ) ) تتابع PHP الفهرسة من حيث توقفت وتستخدم سلاسل نصية عددية كأعداد صحيحة للفهرسة: $foo = [2 => 'apple', 'melon']; // Array( [2] => apple, [3] => melon ) $foo = ['2' => 'apple', 'melon']; // نفس المصفوفة السابقة $foo = [2 => 'apple', 'this is index 3 temporarily', '3' => 'melon']; // نفس المصفوفة السابقة، سيقوم العنصر الأخير بالكتابة فوق العنصر الثاني يمكنك استخدام الصنف splfixedarray لتهيئة المصفوفة بحجم ثابت: $array = new SplFixedArray(3); $array[0] = 1; $array[1] = 2; $array[2] = 3; $array[3] = 4; // RuntimeException // زيادة حجم المصفوفة إلى 10 $array->setSize(10); ملاحظة: المصفوفة المنشأة باستخدام الصف SplFixedArray لها مساحة ذاكرة منخفضة لمجموعات كبيرة من البيانات، ولكن يجب أن تكون المفاتيح أعدادًا صحيحة. لتهيئة مصفوفة بحجم ديناميكي وبعدة عناصر غير فارغة يمكنك استخدام حلقة كالتالي: $myArray = array(); $sizeOfMyArray = 5; $fill = 'placeholder'; for ($i = 0; $i < $sizeOfMyArray; $i++) { $myArray[] = $fill; } print_r($myArray); // Array ( [0] => placeholder [1] => placeholder [2] => placeholder [3] => placeholder [4] => placeholder ) يمكنك استخدام الدالة array_fill() لإنشاء مصفوفة وإعطاء مفاتيحها نفس القيم. array array_fill ( int $start_index , int $num , mixed $value ) تنشأ التعليمة السابقة وتُرجع مصفوفة فيها العدد num من العناصر التي قيمتها value بدءًا من الفهرس start_index، وإذا كانت قيمة start_index سالبة فستبدأ المصفوفة بفهرس سالب ثم تبدأ قيم الفهارس التالية من الصفر. $a = array_fill(5, 6, 'banana'); // Array ( [5] => banana, [6] => banana, ..., [10] => banana) $b = array_fill(-2, 4, 'pear'); // Array ( [-2] => pear, [0] => pear, ..., [2] => pear) نتيجة: عندما تستخدم الدالة array_fill() تكون أكثر محدودية فيما تودّ فعله أما الحلقة فهي أكثر مرونة وتتيح لك الكثير من الخيارات. إذا أردت ملء المصفوفة بمجال أرقام (من 0 إلى 4 مثلًا) يمكنك إما إضافة كل عنصر بشكلٍ مفرد أو استخدام الدالة range(): array range ( mixed $start , mixed $end [, number $step = 1 ] ) تنشأ هذه الدالة مصفوفة تحوي مجال من العناصر، وفيها المعاملان الأول والثاني إلزاميان إذ يحددان بداية ونهاية المجال أما المعامل الثالث فهو خياري ويحدد مقدار زيادة العناصر في المجال. مثال: إنشاء مجال من 0 إلى 4 بمقدار زيادة 1 ستكون نتيجته مصفوفة عناصرها: 0، 1، 2، 3، 4 أما إذا كان مقدار الزيادة 2 فستكون عناصر المصفوفة 0، 2، 4. $array = []; $array_with_range = range(1, 4); for ($i = 1; $i <= 4; $i++) { $array[] = $i; } print_r($array); // Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) print_r($array_with_range); // Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) تعمل الدالة range بشكلٍ صحيح مع الأعداد الصحيحة والعشرية والمنطقية (التي حُوّلت إلى أعداد صحيحة) والسلاسل النصية، ولكن يجب أخذ الحذر عند استخدام الأعداد العشرية كوسيط بسبب مشكلة دقة الفاصلة العائمة. التحقق من وجود مفتاح يمكنك استخدام الدالة array_key_exists() أو isset() أو !empty() للتحقق من وجود مفتاح: $map = [ 'foo' => 1, 'bar' => null, 'foobar' => '', ]; array_key_exists('foo', $map); // true isset($map['foo']); // true !empty($map['foo']); // true array_key_exists('bar', $map); // true isset($map['bar']); // false !empty($map['bar']); // false لاحظ أن الدالة isset() تعامل العنصر الذي قيمته null على أنّه غير موجود، وتعامل !empty() بالمثل أي عنصر قيمته false (استخدام موازنة ضعيفة مثل null، السلسلة الفارغة ' '، 0 تعاملها كلها على أنها false)، أي أن نتيجة isset($map['foobar']) هي true أما نتيجة !empty($map['foobar']) هي false، يمكن أن يؤدي هذا إلى مشاكل (فمن الممكن أن تنسى مثلًا أنّ السلسلة النصية '0' تُعامَل على أنّها false) لذا فإنّ استخدام !empty() يمكن أن يكون غير مقبول. لاحظ أيضًا أنّ isset() و!empty() سيعملان ويعيدان false إذا كانت $map غير معرفة أبدًا وهذا يجعلهم عرضة للخطأ نوعًا ما عند الاستخدام: // "long"و "lang" الفرق بين اسمي المتحولين هو في الكلمتين $my_array_with_a_long_name = ['foo' => true]; array_key_exists('foo', $my_array_with_a_lang_name); // يظهر تحذير isset($my_array_with_a_lang_name['foo']); // false يمكنك أيضًا التحقق في المصفوفات العادية: $ord = ['a', 'b']; // [0 => 'a', 1 => 'b'] المصفوفة السابقة تكافئ array_key_exists(0, $ord); // true array_key_exists(2, $ord); // false لاحظ أنّ أداء isset() أفضل من array_key_exists() لأنّ الأخيرة هي دالة والأولى هي بنية لغوية، ويمكنك أيضًا استخدام الدالة key_exists() التي تعد كنية للدالة array_key_exists(). التحقق من نوع المصفوفة تُرجع الدالة is_array() القيمة true إذا كان المتغير مصفوفة. $integer = 1337; $array = [1337, 42]; is_array($integer); // false is_array($array); // true يمكنك كتابة تلميح في الدالة أنّ نوع المعامل المفروض هو مصفوفة أي أنّ تمرير أي نوع آخر سيسبب خطأً فادحًا. function foo (array $array) { /* هو مصفوفة $array نوع المتغير */ } يمكنك أيضًا استخدام الدالة gettype(). $integer = 1337; $array = [1337, 42]; gettype($integer) === 'array'; // false gettype($array) === 'array'; // true إنشاء مصفوفة من المتغيرات $username = 'Hadibut'; $email = 'hadibut@example.org'; $variables = compact('username', 'email'); // ['username' => 'Hadibut', 'email' => 'hadibut@example.org'] هي $variables قيمة يُستخدم هذا التابع غالبًا في إطارات العمل لتمرير مصفوفة متغيرات بين مكونين. التحقق من وجود قيمة في مصفوفة تُرجع الدالة in_array() القيمة true إذا كان العنصر موجودًا في المصفوفة. $fruits = ['banana', 'apple']; $foo = in_array('banana', $fruits); //true هي $foo قيمة $bar = in_array('orange', $fruits); //false هي $bar قيمة ويمكنك أن تستخدم الدالة array_search() لتحصل على مفتاح عنصر ما من المصفوفة. $userdb = ['Sandra Shush', 'Stefanie Mcmohn', 'Michael']; $pos = array_search('Stefanie Mcmohn', $userdb); if ($pos !== false) { echo "Stefanie Mcmohn found at $pos"; } الإصدار PHP 5.x من 5.5 وما فوق يمكنك بدءًا من الإصدار PHP 5.5 وما بعده استخدام array_column() بالتزامن مع array_search() ويعدّ هذا مفيدًا بشكلٍ خاص للتحقق من وجود قيمة في المصفوفة الترابطية: $userdb = [ [ "uid" => '100', "name" => 'Sandra Shush', "url" => 'urlof100', ], [ "uid" => '5465', "name" => 'Stefanie Mcmohn', "pic_square" => 'urlof100', ], [ "uid" => '40489', "name" => 'Michael', "pic_square" => 'urlof40489', ] ]; $key = array_search(40489, array_column($userdb, 'uid')); واجهات ArrayAccess وIterator الميزة المفيدة الأخرى هي الوصول إلى تجميعات الكائن المخصص كمصفوفات في PHP، يوجد واجهتين في PHP بدءًا من الإصدار PHP 5.0.0 لدعم ذلك وهما: ArrayAccess وIterator يتيحان لك الوصول إلى الكائنات المخصصة على أنّها مصفوفات. ArrayAccess بفرض لدينا الصنف user يعبر عن مستخدم وجدول في قاعدة البيانات يخزن كل المستخدمين ونريد إنشاء الصنف UserCollection ليقوم بما يلي: يسمح لنا بمخاطبة مستخدم عن طريق معرف الاسم الفريد. أداء العمليات الأساسية (ليس كل عمليات CRUD - الإنشاء والقراءة والتحديث والحذف - إنما على الأقل الإنشاء والاستعادة والحذف) على مجموعة المستخدمين لدينا. انتبه أننا نستخدم الصياغة القصيرة لإنشاء مصفوفة [] والمتاحة بدءًا من الإصدار 5.4: class UserCollection implements ArrayAccess { protected $_conn; protected $_requiredParams = ['username','password','email']; public function __construct() { $config = new Configuration(); $connectionParams = [ // معلومات الاتصال بقاعدة البيانات ]; $this->_conn = DriverManager::getConnection($connectionParams, $config); } protected function _getByUsername($username) { $ret = $this->_conn->executeQuery('SELECT * FROM `User` WHERE `username` IN (?)', [$username])->fetch(); return $ret; } // ArrayAccess بدء التوابع المطلوبة من public function offsetExists($offset) { return (bool) $this->_getByUsername($offset); } public function offsetGet($offset) { return $this->_getByUsername($offset); } public function offsetSet($offset, $value) { if (!is_array($value)) { throw new \Exception('value must be an Array'); } $passed = array_intersect(array_values($this->_requiredParams), array_keys($value)); if (count($passed) < count($this->_requiredParams)) { throw new \Exception('value must contain at least the following params: ' .implode(',', $this->_requiredParams)); } $this->_conn->insert('User', $value); } public function offsetUnset($offset) { if (!is_string($offset)) { throw new \Exception('value must be the username to delete'); } if (!$this->offsetGet($offset)) { throw new \Exception('user not found'); } $this->_conn->delete('User', ['username' => $offset]); } // ArrayAccess بدء التوابع المطلوبة من } بعد ذلك يمكننا كتابة: $users = new UserCollection(); var_dump(empty($users['testuser']),isset($users['testuser'])); $users['testuser'] = ['username' => 'testuser', 'password' => 'testpassword', 'email' => 'test@test.com']; var_dump(empty($users['testuser']), isset($users['testuser']), $users['testuser']); unset($users['testuser']); var_dump(empty($users['testuser']), isset($users['testuser'])); بفرض أنه لا يوجد لدينا المستخدم testuser قبل تنفيذ الشيفرة فسيكون لدينا الخرج التالي: bool(true) bool(false) bool(false) bool(true) array(17) { ["username"]=> string(8) "testuser" ["password"]=> string(12) "testpassword" ["email"]=> string(13) "test@test.com" } bool(true) bool(false) ملاحظة: لا تُستدعى الدالة offsetExists عندما تريد التحقق من وجود مفتاح مع دالة array_key_exists، لذا فإنّ الشيفرة التالية سيكون خرجها false مرتين: var_dump(array_key_exists('testuser', $users)); $users['testuser'] = ['username' => 'testuser', 'password' => 'testpassword', 'email' => 'test@test.com']; var_dump(array_key_exists('testuser', $users)); Iterator سنوسع الصنف في الأعلى بإضافة عدة دوال من الواجهة Iterator لنستطيع استخدام التكرار مع foreach وwhile. نحتاج أولًا إلى خاصيّة تحتوي الفهرس الحالي للمكرِّر، سنسميها $_position ونضيفها إلى خاصيّات الصنف: // Iterator موقع المكرِّر الحالي مطلوب من توابع الواجهة protected $_position = 1; ثم نضيف الواجهة Iterator إلى قائمة الواجهات التي سينفذها صنفنا: class UserCollection implements ArrayAccess, Iterator { ثم نضيف المطلوب من دوال الواجهة Iterator: // Iterator بدء التوابع المطلوبة من الواجهة public function current () { return $this->_getById($this->_position); } public function key () { return $this->_position; } public function next () { $this->_position++; } public function rewind () { $this->_position = 1; } public function valid () { return null !== $this->_getById($this->_position); } // Iterator نهاية التوابع المطلوبة من الواجهة أصبح لدينا مصدر كامل لتنفيذ الصنف لكِلا الواجهتين، لاحظ أنّ هذا المثال ليس مثاليًا لأنّ المعرفات في قاعدة البيانات قد لا تكون تسلسلية لكن كتبناه لإعطائك فكرةً أساسيةً وهي أنّه بإمكانك مخاطبة تجميعات الكائنات بأي طريقة ممكنة عن طريق تنفيذ الواجهتين ArrayAccess وIterator. class UserCollection implements ArrayAccess, Iterator { // Iterator موقع المكرِّر الحالي المطلوب من توابع الواجهة protected $_position = 1; // <إضافة التوابع القديمة من الشيفرة السابقة هنا> // Iterator بدء التوابع المطلوبة من الواجهة public function current () { return $this->_getById($this->_position); } public function key () { return $this->_position; } public function next () { $this->_position++; } public function rewind () { $this->_position = 1; } public function valid () { return null !== $this->_getById($this->_position); } // Iterator نهاية التوابع المطلوبة من الواجهة } ولكتابة حلقة تمر على كل الكائنات من نوع user باستخدام foreach: foreach ($users as $user) { var_dump($user['id']); } سينتج عن الشيفرة السابقة الخرج التالي: string(2) "1" string(2) "2" string(2) "3" string(2) "4" ... تكرار عدة مصفوفات معًا قد نحتاج أحيانًا إلى تكرار مصفوفتين لهما نفس الطول معًا مثال: $people = ['Tim', 'Tony', 'Turanga']; $foods = ['chicken', 'beef', 'slurm']; أبسط طريقة لتنفيذ ذلك هي استخدام الدالة array_map: array_map(function($person, $food) { return "$person likes $food\n"; }, $people, $foods); سينتج عن ذلك الخرج التالي: assert(count($people) === count($foods)); for ($i = 0; $i < count($people); $i++) { echo "$people[$i] likes $foods[$i]\n"; } إذا لم يكن للمصفوفتين مفاتيح تكرارية يمكننا استخدام array_values($array)[$i] بدلًا من $array[$i]، وإذا كان لهما نفس ترتيب المفاتيح يمكننا استخدام حلقة foreach مع مفتاح على إحداهما: foreach ($people as $index => $person) { $food = $foods[$index]; echo "$person likes $food\n"; } يمكن تكرار المصفوفات المستقلة فقط إذا كان لها نفس الطول ونفس اسم المفتاح، أي أنّه إذا لم يكن للمصفوفات مفاتيحًا فالمفاتيح مرقمة وهذا جيّد أو يمكنك تسمية المفاتيح ووضعهم في نفس الترتيب لكل مصفوفة، ويمكنك أيضًا استخدام الدالة array_combine. $combinedArray = array_combine($people, $foods); // $combinedArray = ['Tim' => 'chicken', 'Tony' => 'beef', 'Turanga' => 'slurm']; بعدها يمكنك كتابة حلقة تكرار كما في السابق: foreach ($combinedArray as $person => $meal) { echo "$person likes $meal\n"; } استخدام فهرس تزايدي تعمل هذه الطريقة على زيادة عدد صحيح من 0 وحتى أكبر فهرس في المصفوفة. $colors = ['red', 'yellow', 'blue', 'green']; for ($i = 0; $i < count($colors); $i++) { echo 'I am the color ' . $colors[$i] . '<br>'; } يمكنك أيضًا المرور على جميع عناصر المصفوفة بترتيب عكسي دون استخدام الدالة array_reverse التي قد تزيد الحِمل إذا كانت المصفوفة كبيرة. $colors = ['red', 'yellow', 'blue', 'green']; for ($i = count($colors) - 1; $i >= 0; $i--) { echo 'I am the color ' . $colors[$i] . '<br>'; } يمكنك تخطي أو إرجاع الفهرس بسهولة مستخدمًا هذه الطريقة. $array = ["alpha", "beta", "gamma", "delta", "epsilon"]; for ($i = 0; $i < count($array); $i++) { echo $array[$i], PHP_EOL; if ($array[$i] === "gamma") { $array[$i] = "zeta"; $i -= 2; } elseif ($array[$i] === "zeta") { $i++; } } الخرج: alpha beta gamma beta zeta epsilon لا يمكن أن نقوم بهذا بشكلٍ مباشر في المصفوفات التي لا تحوي فهارس تزايدية (بما في ذلك المصفوفات التي تكون فهارسها بترتيب عكسي مثل: [1 => "foo", 0 => "bar"], ["foo" => "f", "bar" => "b"])، لذا نستخدم الدوال array_values أو array_keys: $array = ["a" => "alpha", "b" => "beta", "c" => "gamma", "d" => "delta"]; $keys = array_keys($array); for ($i = 0; $i < count($array); $i++) { $key = $keys[$i]; $value = $array[$key]; echo "$value is $key\n"; } استخدام مؤشرات المصفوفة الداخلية تحوي كل نسخة مصفوفة مؤشرًا داخليًا، يمكن باستخدام هذا المؤشر استعادة عناصر مختلفة من المصفوفة عن طريق نفس الاستدعاء في مرات مختلفة. استخدام الدالة each يُرجع كل استدعاء للدالة مفتاح وقيمة عنصر المصفوفة الحالي ويزيد مؤشر المصفوفة الداخلي. $array = ["f" => "foo", "b" => "bar"]; while (list($key, $value) = each($array)) { echo "$value begins with $key"; } استخدام الدالة next $array = ["Alpha", "Beta", "Gamma", "Delta"]; while (($value = next($array)) !== false) { echo "$value\n"; } لاحظ أنّ هذا المثال يفترض أنّه لا يوجد عناصر في المصفوفة معرّفة بالقيمة المنطقية false، لمنع هذا الافتراض نستخدم الدالة key للتحقق من وصول المؤشر الداخلي إلى نهاية المصفوفة: $array = ["Alpha", "Beta", "Gamma", "Delta"]; while (key($array) !== null) { echo current($array) . PHP_EOL; next($array); } ويسهّل هذا أيضًا تكرار المصفوفة دون حلقة مباشرة: class ColorPicker { private $colors = ["#FF0064", "#0064FF", "#64FF00", "#FF6400", "#00FF64", "#6400FF"]; public function nextColor() : string { $result = next($colors); // إذا وصلت لنهاية المصفوفة if (key($colors) === null) { reset($colors); } return $result; } } استخدام foreach حلقة مباشرة foreach ($colors as $color) { echo "I am the color $color<br>"; } حلقة مع مفاتيح $foods = ['healthy' => 'Apples', 'bad' => 'Ice Cream']; foreach ($foods as $key => $food) { echo "Eating $food is $key"; } حلقة بالمرجعية إنّ تعديل قيمة ($color أو $food) في الأمثلة السابقة بشكلٍ مباشر لا يغيّر قيمتها في المصفوفة إنّما يجب استخدام العامل & لتصبح القيمة هي مؤشر مرجعي للعنصر في المصفوفة. $years = [2001, 2002, 3, 4]; foreach ($years as &$year) { if ($year < 2000) $year += 2000; } الشيفرة السابقة مماثلة للشيفرة التالية: $years = [2001, 2002, 3, 4]; for($i = 0; $i < count($years); $i++) { $year = &$years[$i]; if($year < 2000) $year += 2000; } التزامن يمكن تعديل مصفوفات PHP أثناء التكرار بدون مشاكل تزامن، إذا كانت المصفوفة تُكرَّر بالمرجع فإنّ التكرارات اللاحقة ستتأثر بتغييرات المصفوفة وإلا فإنّها لن تتأثر (كما لو أنّك تكرر نسخة من المصفوفة نفسها)، وازن التكرار بالقيمة: $array = [0 => 1, 2 => 3, 4 => 5, 6 => 7]; foreach ($array as $key => $value) { if ($key === 0) { $array[6] = 17; unset($array[4]); } echo "$key => $value\n"; } الخرج: 0 => 1 2 => 3 4 => 5 6 => 7 أما إذا كانت المصفوفة تُكرَّر بالمرجع: $array = [0 => 1, 2 => 3, 4 => 5, 6 => 7]; foreach ($array as $key => &$value) { if ($key === 0) { $array[6] = 17; unset($array[4]); } echo "$key => $value\n"; } الخرج: 0 => 1 2 => 3 6 => 17 لن تُكرَّر مجموعة القيمة-المفتاح 4 => 5 وستتغير 6 => 7 إلى 6 => 17. استخدام المكرِّر ArrayObject يسمح لك الصنف arrayiterator بتعديل وإلغاء ضبط القيم أثناء تكرار المصفوفات والكائنات. مثال: $array = ['1' => 'apple', '2' => 'banana', '3' => 'cherry']; $arrayObject = new ArrayObject($array); $iterator = $arrayObject->getIterator(); for($iterator; $iterator->valid(); $iterator->next()) { echo $iterator->key() . ' => ' . $iterator->current() . "</br>"; } الخرج: 1 => apple 2 => banana 3 => cherry ترجمة -وبتصرف- للفصول [Arrays - Array iteration] من كتاب PHP Notes for Professionals book اقرأ أيضًا المقال التالي: التنفيذ على المصفوفات والتعامل معها في PHP المقال السابق: المراجع (references) في PHP1 نقطة
-
تطبيق دالة على كل عنصر من عناصر المصفوفة نستخدم الدالة array_map() لتطبيق دالة على جميع عناصر مصفوفة ما، وتُرجع لنا مصفوفة جديدة: $array = array(1,2,3,4,5); // يتم المرور على كل عنصر من عناصر المصفوفة ويُخزَّن في معامل الدالة $newArray = array_map(function($item) { return $item + 1; }, $array); // array(2,3,4,5,6) الآن $newArray قيمة يمكنك استخدام دالة ذات اسم بدلًا من استخدام دالة مجهولة الاسم، فنكتب الشيفرة السابقة كالتالي: function addOne($item) { return $item + 1; } $array = array(1, 2, 3, 4, 5); $newArray = array_map('addOne', $array); إذا كانت الدالة المسمّاة هي تابع صنف فيجب تضمين مرجع إلى كائن الصنف الذي يعود التابع له عند استدعاء الدالة: class Example { public function addOne($item) { return $item + 1; } public function doCalculation() { $array = array(1, 2, 3, 4, 5); $newArray = array_map(array($this, 'addOne'), $array); } } الطريقة الثانية لتطبيق دالة على عناصر المصفوفة هي استخدام الدالة array_walk() والدالة array_walk_recursive()، يُمرَّر رد النداء (callback) إلى هاتين الدالتين ويأخذ مفتاح/فهرس وقيمة كل عنصر من عناصر المصفوفة، لا ترجع هذه الدوال دوالًا جديدة إنّما تُرجع قيمة منطقية تدل على نجاح تنفيذ الدالة، يمكننا مثلًا طباعة كل عنصر في المصفوفة بكتابة الشيفرة التالية: $array = array(1, 2, 3, 4, 5); array_walk($array, function($value, $key) { echo $value . ' '; }); // "1 2 3 4 5" يمكن تمرير قيمة معامل رد النداء بالمرجعية مما يسمح لك بتمرير القيمة مباشرةً في المصفوفة الأصلية: $array = array(1, 2, 3, 4, 5); array_walk($array, function(&$value, $key) { $value++; }); // array(2,3,4,5,6) الآن $array قيمة نستخدم الدالة array_walk_recursive() مع المصفوفات المتداخلة (nested) للتعامل مع كل مصفوفة فرعية: $array = array(1, array(2, 3, array(4, 5), 6); array_walk_recursive($array, function($value, $key) { echo $value . ' '; }); // "1 2 3 4 5 6" لاحظ أنّ الدالتين array_walk() وarray_walk_recursive() يسمحان لك بتغيير قيمة عناصر المصفوفة وليس المفاتيح، كما أنّه يمكنك تمرير المفاتيح بالمرجع في رد النداء لكن هذا ليس له تأثير. تقسيم المصفوفة إلى أجزاء (chunks) تقسّم الدالة array_chunk() المصفوفة إلى أجزاء صغيرة. بفرض لدينا الدالة أحادية البعد التالية: $input_array = array('a', 'b', 'c', 'd', 'e'); وطبّقنا الدالة array_chunk() عليها: $output_array = array_chunk($input_array, 2); تقسّم الشيفرة السابقة المصفوفة إلى مصفوفات أصغر تحتوي كلّ منها عنصرين وتنشئ مصفوفة جديدة متعددة الأبعاد: Array ( [0] => Array ( [0] => a [1] => b ) [1] => Array ( [0] => c [1] => d ) [2] => Array ( [0] => e ) ) إذا لم يكن من الممكن تقسيم عناصر المصفوفة بالتساوي وفقًا للحجم المحدد فإنّ المصفوفة الأخيرة ستحمل العناصر الباقية وتكون أصغر من باقي المصفوفات. إذا مررنا للوسيط الثاني عددًا أصغر من 1 سيُرمى E_WARNING وستكون مصفوفة الخرج NULL. المعامل تفاصيل $array (array) مصفوفة الدخل التي نريد العمل عليها $size (int) حجم المصفوفة الصغيرة (عدد صحيح) $preserve_keys (boolean) (اختياري) FALSE وإلا TRUE إذا أردت أن تحافظ المصفوفات الصغيرة على المفاتيح تكون قيمته table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } تجميع عناصر مصفوفة لتشكيل سلسلة نصية تجمع الدالة implode() كل قيم المصفوفة لكنها تفقد معلومات المفاتيح: $arr = ['a' => "AA", 'b' => "BB", 'c' => "CC"]; echo implode(" ", $arr); // AA BB CC نستطيع تجميع المفاتيح باستدعاء الدالة array_keys(): $arr = ['a' => "AA", 'b' => "BB", 'c' => "CC"]; echo implode(" ", array_keys($arr)); // a b c يعدّ تجميع القيم مع المفاتيح أكثر تعقيدًا لكن يمكن القيام به بنمط الدالة: $arr = ['a' => "AA", 'b' => "BB", 'c' => "CC"]; echo implode(" ", array_map(function($key, $val) { // دالة تربط المفتاح بالقيمة return "$key:$val"; }, array_keys($arr), $arr)); // a:AA b:BB c:CC تفكيك مصفوفة باستخدام list() يمكن استخدام الدالة list() لإسناد قائمة من قيم المتغيرات إلى مصفوفة. كما يمكنك الاطلاع على الدالة compact(). // بمفاتيح مرقمة تبدأ من الصفر $array إلى عناصر المصفوفة $a,$b,$c إسناد قيم المتغيرات list($a, $b, $c) = $array; بدءًا من الإصدار 7.1 يمكنك استخدام الصيغة المختصرة: // بمفاتيح مرقمة تبدأ من الصفر $array إلى عناصر المصفوفة $a,$b,$c إسناد قيم المتغيرات [$a, $b, $c] = $array; //"a", "b", "c" والمفاتيح $array إلى عناصر المصفوفة $a,$b,$c إسناد قيم المتغيرات ["a" => $a, "b" => $b, "c" => $c] = $array; الدالة array_reduce تختزل الدالة array_reduce المصفوفة إلى قيمة واحدة. تمرّ الدالة على كل عنصر من عناصر المصفوفة بنتيجة مُعادة من التكرار السابق وتنتج قيمة جديدة تحتفظ بها للتكرار التالي. الاستخدام: array_reduce ($array, function($carry, $item){...}, $defaul_value_of_first_carry) $carry هي النتيجة المعادة من التكرار السابق و$item يحمل قيمة التكرار الحالي. مجموع مصفوفة $result = array_reduce([1, 2, 3, 4, 5], function($carry, $item){ return $carry + $item; }); // 15 أكبر عدد في المصفوفة $result = array_reduce([10, 23, 211, 34, 25], function($carry, $item){ return $item > $carry ? $item : $carry; }); // 211 هل كل العناصر أكبر من 100 $result = array_reduce([101, 230, 210, 341, 251], function($carry, $item){ return $carry && $item > 100; }, true); // true يجب أن تكون القيمة الافتراضية // true هل يوجد عنصر أصغر من 100 $result = array_reduce([101, 230, 21, 341, 251], function($carry, $item){ return $carry || $item < 100; }, false); // false يجب أن تكون القيمة الافتراضية // true تجميع مصفوفة $result = array_reduce(["hello", "world", "PHP", "language"], function($carry, $item){ return !$carry ? $item : $carry . "-" . $item ; }); // "hello-world-PHP-language" يمكنك أيضًا كتابة تابع للتجميع: function implode_method($array, $piece){ return array_reduce($array, function($carry, $item) use ($piece) { return !$carry ? $item : ($carry . $piece . $item); }); } $result = implode_method(["hello", "world", "PHP", "language"], "-"); // "hello-world-PHP-language" دفع عنصر إلى نهاية مصفوفة يوجد طريقتين لدفع عنصر إلى مصفوفة: الدالة array_push() و$array[] =. نستخدم الدالة array_push() كما يلي: $array = [1,2,3]; $newArraySize = array_push($array, 5, 6); // يُرجع التابع الحجم الجديد للمصفوفة print_r($array); // تُمرّر المصفوفة بالمرجع لذا تُعدّل المصفوفة الأساسية لتحتوي العناصر الجديدة ستطبع الشيفرة: Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 5 [4] => 6 ) نستخدم $array[] = كالتالي: $array = [1,2,3]; $array[] = 5; $array[] = 6; print_r($array); تطبع الشيفرة السابقة: Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 5 [4] => 6 ) ترشيح (Filtering) مصفوفة نستخدم الدالة array_filter لترشيح قيم من المصفوفة والحصول على مصفوفة جديدة تحقق كل شروط المُرشِّح. ترشيح القيم غير الفارغة أفضل حالة للترشيح هي إزالة جميع القيم الفارغة: $my_array = [1,0,2,null,3,'',4,[],5,6,7,8]; $non_empties = array_filter($my_array); // $non_empties will contain // [1,2,3,4,5,6,7,8] هو $non_empties يصبح محتوى المصفوفة الترشيح برد النداء بفرض أننا نريد الحصول على القيم الزوجية فقط: $my_array = [1,2,3,4,5,6,7,8]; $even_numbers = array_filter($my_array, function($number) { return $number % 2 === 0; }); تستقبل الدالة array_filter المصفوفة التي نريد ترشيحها في الوسيط الأول أما الوسيط الثاني فهو المُرشِّح الذي نريد تطبيقه. الإصدار PHP 5.6 وما بعده الترشيح تبعًا للفهرس يمكننا تمرير معامل ثالث للدالة array_filter يسمح بتعديل القيم الممررة إلى رد النداء، يمكن ضبط هذا المعامل على أنّه ARRAY_FILTER_USE_KEY أو ARRAY_FILTER_USE_BOTH، وهو يسمح أن يستقبل رد النداء المفاتيح بدلًا من القيم أو كلًا من المفاتيح والقيم كوسطاء. فإذا أردت التعامل مع الفهارس بدلًا من القيم مثلًا: $numbers = [16,3,5,8,1,4,6]; $even_indexed_numbers = array_filter($numbers, function($index) { return $index % 2 === 0; }, ARRAY_FILTER_USE_KEY); الفهارس في المصفوفة المرشَّحة لاحظ أنّ الدالة array_filter تحافظ على مفاتيح المصفوفة الأصلية، ومن الخطأ أن تقوم بتنفيذ حلقة for على المصفوفة المُرشَّحة: $my_array = [1,0,2,null,3,'',4,[],5,6,7,8]; $filtered = array_filter($my_array); // إظهار كل الأخطاء والملاحظات error_reporting(E_ALL); // for من الخطأ استخدام حلقة for ($i = 0; $i < count($filtered); $i++) { print $filtered[$i]; } /* :الخرج 1 Notice: Undefined offset: 1 2 Notice: Undefined offset: 3 3 Notice: Undefined offset: 5 4 Notice: Undefined offset: 7 */ حدث هذا لأن قيم الفهارس 1، 3، 5، 7 التي هي 0، null، سلسلة فارغة ''، مصفوفة فارغة [] على الترتيب قد حُذفت مع فهارسها. إذا كنت تحتاج تمرير حلقة على نتيجة مُرشِّح طُبِّق على مصفوفة مفهرسة، يجب أن تستدعي الدالة array_values لنتيجة الدالة array_filter لتُنشئ مصفوفة جديدة مع فهارس صحيحة: $my_array = [1,0,2,null,3,'',4,[],5,6,7,8]; $filtered = array_filter($my_array); $iterable = array_values($filtered); error_reporting(E_ALL); // إظهار كل الأخطاء والملاحظات for ($i = 0; $i < count($iterable); $i++) { print $iterable[$i]; } // لا يوجد تحذيرات إزالة عناصر من مصفوفة لإزالة عنصر من مصفوفة مثلًا العنصر الذي فهرسه 1: $fruit = array("bananas", "apples", "peaches"); unset($fruit[1]); ستزيل الشيفرة السابقة العنصر apples من المصفوفة لكن لاحظ أنّ الدالة unset لا تغيّر فهارس باقي العناصر لذا فإنّ المصفوفة $fruit تحتوي الآن الفهارس 0 و2. يمكنك الحذف في المصفوفات الترابطية كما يلي: $fruit = array('banana', 'one'=>'apple', 'peaches'); print_r($fruit); /* Array ( [0] => banana [one] => apple [1] => peaches ) */ unset($fruit['one']); تصبح الآن المصفوفة $fruit: print_r($fruit); /* Array ( [0] => banana [1] => peaches ) */ لاحظ أنّ الشيفرة التالية: unset($fruit); تزيل تعيين المتغير لذا تحذف كامل المصفوفة أي أنّه لم يعد بإمكاننا الوصول إلى أي عنصر من عناصرها. إزالة عناصر الأطراف تُزيل الدالة array_shift() عنصرًا من بداية المصفوفة. مثال: $fruit = array("bananas", "apples", "peaches"); array_shift($fruit); print_r($fruit); الخرج: Array ( [0] => apples [1] => peaches ) تُزيل الدالة array_pop() عنصرًا من نهاية المصفوفة. مثال: $fruit = array("bananas", "apples", "peaches"); array_pop($fruit); print_r($fruit); الخرج: Array ( [0] => bananas [1] => apples ) ترتيب مصفوفة يوجد عدة دوال لترتيب المصفوفات في PHP: الدالة sort() ترتّب هذه الدالة عناصر المصفوفة ترتيبًا تصاعديًّا وفقًا لقيمها. $fruits = ['Zitrone', 'Orange', 'Banane', 'Apfel']; sort($fruits); print_r($fruits); /* Array ( [0] => Apfel [1] => Banane [2] => Orange [3] => Zitrone ) */ الدالة rsort() ترتّب هذه الدالة عناصر المصفوفة ترتيبًا تنازليًّا وفقًا لقيمها. $fruits = ['Zitrone', 'Orange', 'Banane', 'Apfel']; rsort($fruits); print_r($fruits); /* Array ( [0] => Zitrone [1] => Orange [2] => Banane [3] => Apfel ) */ الدالة asort() تُرتِّب هذه الدالة عناصر المصفوفة ترتيبًا تصاعديًّا وتحافظ على اقتران الفهارس مع القيم المرتبطة بها. $fruits = [1 => 'lemon', 2 => 'orange', 3 => 'banana', 4 => 'apple']; asort($fruits); print_r($fruits); /* Array ( [4] => apple [3] => banana [1] => lemon [2] => orange ) */ الدالة arsort() تُرتِّب هذه الدالة عناصر المصفوفة ترتيبًا تنازليًّا وتحافظ على اقتران الفهارس مع القيم المرتبطة بها. $fruits = [1 => 'lemon', 2 => 'orange', 3 => 'banana', 4 => 'apple']; arsort($fruits); print_r($fruits); /* Array ( [2] => orange [1] => lemon [3] => banana [4] => apple ) */ الدالة ksort() تُرتّب هذه الدالة المصفوفة ترتيبًا تصاعديًّا حسب المفتاح. $fruits = ['d'=>'lemon', 'a'=>'orange', 'b'=>'banana', 'c'=>'apple']; ksort($fruits); print_r($fruits); /* Array ( [a] => orange [b] => banana [c] => apple [d] => lemon ) */ الدالة krsort() تُرتّب هذه الدالة المصفوفة ترتيبًا تنازليًّا حسب المفتاح. $fruits = ['d'=>'lemon', 'a'=>'orange', 'b'=>'banana', 'c'=>'apple']; krsort($fruits); print_r($fruits); /* Array ( [d] => lemon [c] => apple [b] => banana [a] => orange ) */ الدالة natsort() ترتّب هذه الدالة المصفوفة بنفس الطريقة التي يرتّب بها الإنسان (ترتيب طبيعي). $files = ['File8.stack', 'file77.stack', 'file7.stack', 'file13.stack', 'File2.stack']; natsort($files); print_r($files); /* Array ( [4] => File2.stack [0] => File8.stack [2] => file7.stack [3] => file13.stack [1] => file77.stack ) */ الدالة natcasesort() ترتّب هذه الدالة المصفوفة بنفس الطريقة التي يرتّب بها الإنسان (ترتيب طبيعي) لكن غير حساسة لحالة الأحرف. $files = ['File8.stack', 'file77.stack', 'file7.stack', 'file13.stack', 'File2.stack']; natcasesort($files); print_r($files); /* Array ( [4] => File2.stack [2] => file7.stack [0] => File8.stack [3] => file13.stack [1] => file77.stack ) */ الدالة shuffle() تخلط هذه الدالة عناصر المصفوفة (ترتيب عشوائي). $array = ['aa', 'bb', 'cc']; shuffle($array); print_r($array); بما أنّ الترتيب عشوائي فليس هناك نتيجة واحدة محددة للشيفرة السابقة، فقد تكون مثلًا: Array ( [0] => cc [1] => bb [2] => aa ) الدالة usort() ترتّب هذه الدالة المصفوفة باستخدام دالة موازنة معرّفة من قبل المستخدم. function compare($a, $b) { if ($a == $b) { return 0; } return ($a < $b) ? -1 : 1; } $array = [3, 2, 5, 6, 1]; usort($array, 'compare'); print_r($array); /* Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 5 [4] => 6 ) */ الدالة uasort() ترتّب هذه الدالة المصفوفة باستخدام دالة موازنة معرّفة من قبل المستخدم وتحافظ على ارتباط الفهارس بالقيم المقابلة. function compare($a, $b) { if ($a == $b) { return 0; } return ($a < $b) ? -1 : 1; } $array = ['a' => 1, 'b' => -3, 'c' => 5, 'd' => 3, 'e' => -5]; uasort($array, 'compare'); print_r($array); /* Array ( [e] => -5 [b] => -3 [a] => 1 [d] => 3 [c] => 5 ) */ الدالة uksort() ترتّب هذه الدالة المصفوفة حسب المفاتيح باستخدام دالة موازنة معرّفة من قبل المستخدم. function compare($a, $b) { if ($a == $b) { return 0; } return ($a < $b) ? -1 : 1; } $array = ['ee' => 1, 'g' => -3, '4' => 5, 'k' => 3, 'oo' => -5]; uksort($array, 'compare'); print_r($array); /* Array ( [ee] => 1 [g] => -3 [k] => 3 [oo] => -5 [4] => 5 ) */ إضافة بعض مفاتيح المصفوفة إلى اللائحة البيضاء يمكنك استخدام الدالتين array_intersect_key وarray_flip معًا عندما تريد السماح بوجود مفاتيح محددة فقط في المصفوفة خاصةً عندما تأتي المفاتيح من معاملات طلب ما. $parameters = ['foo' => 'bar', 'bar' => 'baz', 'boo' => 'bam']; $allowedKeys = ['foo', 'bar']; $filteredParameters = array_intersect_key($parameters, array_flip($allowedKeys)); // $filteredParameters = ['foo' => 'bar', 'bar' => 'baz] إذا لم يحتوي متغير المعاملات على أي مفتاح مسموح به فإنّ المتغير filteredParameters سيكون مصفوفة فارغة. ويمكنك أيضًا استخدام الدالة array_filter لنفس الوظيفة بدءًا من الإصدار PHP 5.6، ممررًا الراية ARRAY_FILTER_USE_KEY كمعامل ثالث. $parameters = ['foo' => 1, 'hello' => 'world']; $allowedKeys = ['foo', 'bar']; $filteredParameters = array_filter( $parameters, function ($key) use ($allowedKeys) { return in_array($key, $allowedKeys); }, ARRAY_FILTER_USE_KEY ); يوفر استخدام الدالة arrayfilter مرونةً إضافيةً لإجراء اختبار عشوائي على المفاتيح، فمثلًا يمكن أن تحتوي المصفوفة $allowedKeys أنماطًا تعبيرية بدلًا من السلاسل النصية العادية، كما أنّها تذكر الهدف من الشيفرة بشكلٍ أكثر صراحة من الدالتين array_intersect_key وarray_flip. إضافة عنصر إلى بداية المصفوفة يمكنك استخدام الدالة array_unshift() عند الحاجة إلى إضافة عنصر في بداية المصفوفة دون تغيير في ترتيب العناصر الحالية، إذ تضيف هذه الدالة العناصر الممررة بالكامل إلى بداية المصفوفة فتبقى العناصر الممررة بنفس الترتيب، وتعدَّل جميع المفاتيح العددية لتبدأ من الصفر بينما تبقى المفاتيح التي تكون بشكل سلسلة نصية كما هي. $myArray = array(1, 2, 3); array_unshift($myArray, 4); print_r($myArray); // Array ( [0] => 4 [1] => 1 [2] => 2 [3] => 3 ) بما أنّ الدالة array_unshift() تُجبر المصفوفة على إعادة تعيين أزواج مفتاح - قيمة والعنصر الجديد يجعل مفاتيح المداخل التالية تبدأ من n+1 فمن الأسهل إنشاء مصفوفة جديدة وإضافة المصفوفة الحالية إلى المصفوفة المُنشأة حديثًا. مثال: $myArray = array('apples', 'bananas', 'pears'); $myElement = array('oranges'); $joinedArray = $myElement; foreach ($myArray as $i) { $joinedArray[] = $i; } // Array ( [0] => oranges [1] => apples [2] => bananas [3] => pears ) يمكنك الاطلاع على المثال هنا تبادل القيم مع المفاتيح تستبدل الدالة array_flip() كل مفاتيح المصفوفة بالقيم المرتبطة معها. $colors = array( 'one' => 'red', 'two' => 'blue', 'three' => 'yellow', ); array_flip($colors); /* array( 'red' => 'one', 'blue' => 'two', 'yellow' => 'three' ) */ دمج مصفوفتين في مصفوفة واحدة تدمج الدالة array_merge() مصفوفتين أو أكثر. $a1 = array("red","green"); $a2 = array("blue","yellow"); print_r(array_merge($a1,$a2)); /* Array ( [0] => red [1] => green [2] => blue [3] => yellow ) */ المصفوفة الترابطية: $a1=array("a"=>"red","b"=>"green"); $a2=array("c"=>"blue","b"=>"yellow"); print_r(array_merge($a1,$a2)); /* Array ( [a] => red [b] => yellow [c] => blue ) */ تُدمج العناصر من مصفوفة أو واحدة أكثر فتُضاف قيم إحداها إلى نهاية المصفوفة الأخرى وتُرجع المصفوفة النهائية. إذا كان لمصفوفات الدخل نفس المفاتيح المكونة من سلاسل نصية فإنّ القيمة اللاحقة للمفتاح ستكتب فوق القيمة السابقة له أما إذا كانت المفاتيح عددية فإنّ القيمة اللاحقة ستُضاف ولن تكتب فوق القيمة السابقة للمفتاح. إذا كانت مفاتيح مصفوفة الدخل رقمية سيُعاد ترقيم القيم في مصفوفة النتيجة بمفاتيح تزايدية ابتداءً من الصفر. معالجة عدة مصفوفات معًا تقاطع (intersection) المصفوفات تُرجع الدالة array_intersect مصفوفة من القيم الموجودة في جميع المصفوفات الممررة إليها وتحافظ على المفاتيح المرتبطة مع القيم المعادة. $array_one = ['one', 'two', 'three']; $array_two = ['two', 'three', 'four']; $array_three = ['two', 'three']; $intersect = array_intersect($array_one, $array_two, $array_three); // $intersect = ['two', 'three'] تتحقق الدالة array_intersect من قيم المصفوفات فقط أما الدالة array_intersect_assoc تُرجع تقاطع القيم مع المفاتيح المرتبطة بها. $array_one = [1 => 'one',2 => 'two',3 => 'three']; $array_two = [1 => 'one', 2 => 'two', 3 => 'two', 4 => 'three']; $array_three = [1 => 'one', 2 => 'two']; $intersect = array_intersect_assoc($array_one, $array_two, $array_three); // $intersect = [1 =>'one',2 => 'two'] تتحقق الدالة array_intersect_key من التقاطع بتفحص المفاتيح فقط وتُرجع المفاتيح الموجودة في جميع المصفوفات الممررة. $array_one = [1 => 'one',2 => 'two',3 => 'three']; $array_two = [1 => 'one', 2 => 'two', 3 => 'four']; $array_three = [1 => 'one', 3 => 'five']; $intersect = array_intersect_key($array_one, $array_two, $array_three); // $intersect = [1 =>'one',3 => 'three'] دمج أو ضم مصفوفتين $fruit1 = ['apples', 'pears']; $fruit2 = ['bananas', 'oranges']; $all_of_fruits = array_merge($fruit1, $fruit2); //$all_of_fruits = [0 => 'apples', 1 => 'pears', 2 => 'bananas', 3 => 'oranges'] لاحظ أنّ الدالة array_merge() ستغيّر الفهارس الرقمية وتكتب فوق الفهارس النصية. $fruit1 = ['one' => 'apples', 'two' => 'pears']; $fruit2 = ['one' => 'bananas', 'two' => 'oranges']; $all_of_fruits = array_merge($fruit1, $fruit2); //$all_of_fruits = ['one' => 'bananas', 'two' => 'oranges'] تعيد الدالة array_merge() كتابة قيم المصفوفة الثانية فوق قيم المصفوفة الأولى إذا لم تستطع إعادة ترقيم فهارسها. يمكنك استخدام العامل + لدمج مصفوفتين بطريقة لا يمكن فيها للقيم أن يُكتب فوقها لكنها لا تُعيد ترقيم الفهارس العددية لذا تخسر قيم المصفوفات التي لها فهرس مُستخدم في المصفوفة الأولى. $fruit1 = ['one' => 'apples', 'two' => 'pears']; $fruit2 = ['one' => 'bananas', 'two' => 'oranges']; $all_of_fruits = $fruit1 + $fruit2; // $all_of_fruits = ['one' => 'apples', 'two' => 'pears'] $fruit1 = ['apples', 'pears']; $fruit2 = ['bananas', 'oranges']; $all_of_fruits = $fruit1 + $fruit2; // $all_of_fruits = [0 => 'apples', 1 => 'pears'] تغيير مصفوفة متعددة الأبعاد إلى مصفوفة ترابطية إذا كان لديك مصفوفة متعددة الأبعاد مثل: [ ['foo', 'bar'], ['fizz', 'buzz'], ] وتريد تغييرها إلى مصفوفة ترابطية مثل: [ 'foo' => 'bar', 'fizz' => 'buzz', ] فيمكنك استخدام الشيفرة التالية: $multidimensionalArray = [ ['foo', 'bar'], ['fizz', 'buzz'], ]; $associativeArrayKeys = array_column($multidimensionalArray, 0); $associativeArrayValues = array_column($multidimensionalArray, 1); $associativeArray = array_combine($associativeArrayKeys, $associativeArrayValues); أو يمكنك تخطي الإعداد $associativeArrayKeys و$associativeArrayValues واستخدام الشيفرة البسيطة التالية: $associativeArray = array_combine(array_column($multidimensionalArray, 0), array_column($multidimensionalArray, 1)); جمع مصفوفتين (المفاتيح من واحدة والقيم من الأخرى) تنشئ الدالة array_combine() مصفوفةً جديدةً عن طريق استخدام المفاتيح من أحد المصفوفات والقيم من مصفوفة أخرى. يبين المثال التالي كيفية دمج مصفوفتين في مصفوفة واحدة ترابطية فيها المفاتيح من المصفوفة الأولى والقيم من المصفوفة الثانية: $array_one = ['key1', 'key2', 'key3']; $array_two = ['value1', 'value2', 'value3']; $array_three = array_combine($array_one, $array_two); var_export($array_three); /* array ( 'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3', ) */ مكتبة PHP المعيارية لهياكل البيانات (SPL) SplFixedArray الاختلاف عن مصفوفة PHP يُنفَّذ نوع المصفوفة الافتراضي في PHP كخرائط hash مرتبة مما يسمح لنا بإنشاء مصفوفات تحتوي على الأزواج قيمة/مفتاح، يمكن أن تكون القيم من أي نوع والمفاتيح إما أرقام أو سلاسل نصية، ومع ذلك فإنّ هذه ليست الطريقة التقليدية لإنشاء المصفوفات. كما تلاحظ في الرسم التوضيحي فإنّه من الممكن أن تُعرض مصفوفة PHP مثل مجموعة مرتبة من الأزواج قيمة/مفتاح حيث يُربَط كل مفتاح إلى أي قيمة. لاحظ أنّه في هذه المصفوفة لدينا مفاتيح عددية ومفاتيح بشكل سلاسل نصية بالإضافة إلى قيم من أنواع مختلفة والمفتاح ليس له تأثير على ترتيب العناصر. $arr = [ 9 => "foo", 1 => 4.2, "bar" => null, ]; foreach($arr as $key => $value) { echo "$key => $value\n"; } تعطينا الشيفرة السابقة ما نتوقعه تمامًا: 9 => foo 1 => 4.2 bar => يُحدد حجم مصفوفات PHP العادية ديناميكيًا، ويزداد وينقص هذا الحجم بإضافة أو حذف عناصر إلى ومن المصفوفة بشكلٍ تلقائي، أما في المصفوفات التقليدية يكون الحجم ثابتًا وتتألف المصفوفة بالكامل من نفس نوع القيم ويمكننا الوصول إلى أي قيمة باستخدام الفهارس بدلًا من المفاتيح ويمكن استنتاج الفهرس من إزاحة القيمة في المصفوفة. وبما أننا سنعرف حجم نوع معين والحجم الثابت للمصفوفة فإنّ الإزاحة هي حجم النوع * n، إذ تمثّل n موضع القيمة في المصفوفة، يعطينا $arr[0] في المثال السابق القيمة 1 ويعطينا العنصر الأول في المصفوفة $arr[1] القيمة 2 وهكذا… ومع ذلك لا يقيّد الصنف SplFixedArray نوع القيم إنما يقيد نوع المفاتيح فقط والتي يجب أن تكون عددية، ?وأيضًا ذات حجم ثابت، وهذا يجعل المصفوفات من النوع SplFixedArray أكثر فعاليةٍ من المصفوفات العادية بطريقةٍ معينة فهي أصغر وتتطلب ذاكرة أقل. تهيئة المصفوفة تُنفَّذ المصفوفة من النوع SplFixedArray ككائن لكن يمكن الوصول إليها بنفس الصياغة التي نصل فيها إلى مصفوفة PHP العادية بما أنّها تنفّذ الواجهة ArrayAccess وتنفّذ أيضًا الواجهات Countable وIterator لذا لها نفس سلوك مصفوفات PHP العادية الذي اعتدت عليه (أي الأشياء مثل count($arr) وforeach($arr as $k => $v) تعمل بنفس الطريقة في المصفوفات من النوع SplFixedArray والمصفوفات العادية). يأخذ باني المصفوفة من النوع SplFixedArray وسيطًا واحدًا يعبّر عن حجم المصفوفة. $arr = new SplFixedArray(4); $arr[0] = "foo"; $arr[1] = "bar"; $arr[2] = "baz"; foreach($arr as $key => $value) { echo "$key => $value\n"; } تعطيك الشيفرة السابقة النتيجة: 0 => foo 1 => bar 2 => baz 3 => كما أنّ الشيفرة: var_dump(count($arr)); تعطينا ما نتوقع: int(4) لاحظ أنه في المصفوفات من النوع SplFixedArray يصف المفتاح ترتيب العنصر في المصفوفة لأنه فهرس حقيقي وليس مجرد خريطة كما في المصفوفات العادية.? تغيير حجم المصفوفة تذكر دائمًا بما أنّ المصفوفة ذات حجم ثابت فإنّ عدّها سيعيد دائمًا القيمة نفسها، لذا طالما أنّ نتيجة unset($arr[1]) هي $arr[1] === null تبقى نتيجة count($arr) هي 4، وتحتاج لتغيير حجم المصفوفة إلى استدعاء التابع setSize: $arr->setSize(3); var_dump(count($arr)); foreach($arr as $key => $value) { echo "$key => $value\n"; } الآن نحصل على النتيجة: int(3) 0 => foo 1 => 2 => baz الاستيراد من SplFixedArray والتصدير إلى SplFixedArray يمكنك استيراد/تصدير مصفوفة عادية إلى/من مصفوفة SplFixedArray باستخدام التوابع fromArray وtoArray. $array = [1,2,3,4,5]; $fixedArray = SplFixedArray::fromArray($array); foreach($fixedArray as $value) { echo $value, "\n"; } // 1 2 3 4 5 $fixedArray = new SplFixedArray(5); $fixedArray[0] = 1; $fixedArray[1] = 2; $fixedArray[2] = 3; $fixedArray[3] = 4; $fixedArray[4] = 5; $array = $fixedArray->toArray(); foreach($array as $value) { echo $value, "\n"; } // 1 2 3 4 5 ترجمة -وبتصرف- للفصول [Executing Upon an Array - Manipulating an Array - Processing Multiple Arrays Together - SPL data structures] من كتاب PHP Notes for Professionals book اقرأ أيضًا المقال التالي: التعامل مع الوقت والتاريخ في PHP المقال السابق: المصفوفات (Arrays) في PHP1 نقطة
-
الصنف Datetime إنشاء نسخة Datetime ثابتة من متغير يمكنك استخدام الشيفرة التالية لإنشاء \DateTimeImmutable في PHP الإصدار 5.6 وما بعده: \DateTimeImmutable::createFromMutable($concrete); أما في الإصدارات السابقة يمكنك استخدام: \DateTimeImmutable::createFromFormat(\DateTime::ISO8601, $mutable->format(\DateTime::ISO8601), $mutable->getTimezone()); إضافة أو طرح تاريخ نستخدم الصنف DateInterval لإضافة أو طرح فترة زمنية في كائن DateTime، إليك المثال التالي الذي نطرح فيه 7 أيام ونطبع رسالة على الشاشة: // التعليمة التالية بدون وسيط تُرجع التاريخ الحالي $now = new DateTime(); // الكائن التالي يمثل فترة زمنية مدتها 7 أيام $interval = new DateInterval('P7D'); // DateTime التعليمة التالية ستُرجع كائن $lastDay = $now->add($interval); // ويُرجع سلسلة نصية DateTime ينسّق هذا التابع كائن $formatedLastDay = $lastDay->format('Y-m-d'); echo "Samara says: Seven Days. You'll be happy on $formatedLastDay."; /* عند تنفيذ هذه الشيفرة في 1-8-2016 يكون الخرج Samara says: Seven Days. You'll be happy on 2016-08-08. */ نستطيع استخدام التابع الفرعي بطريقة مشابهة لطرح التواريخ: $now->sub($interval); echo "Samara says: Seven Days. You were happy last on $formatedLastDay."; /* عند تنفيذ هذه الشيفرة في 1-8-2016 يكون الخرج Samara says: Seven Days. You were happy last on 2016-07-25. */ getTimestamp getTimeStemp هو تمثيل unix لكائن من الصنف datetime. $date = new DateTime(); echo $date->getTimestamp(); سيؤدي ذلك إلى وضع مؤشر صحيح للثواني التي انقضت منذ 00:00:00 UTC، الخميس، 1 كانون الثاني 1970. setDate تضبط هذه الدالة التاريخ لكائن من الصنف datetime. $date = new DateTime(); $date->setDate(2016, 7, 25); تضبط هذه الشيفرة التاريخ ليصبح 25 تموز، 2015 وخرجها: 2016-07-25 17:52:15.819442 إنشاء DateTime من تنسيق معين إنّ PHP قادرة على تحليل عدة تنسيقات للتاريخ، يمكنك استخدام التابع الساكن DateTime::createFromFormat إذا أردت تحليل تنسيق غير معياري أو إذا أردت أن تحدد في شيفرتك تنسيق التاريخ المُستخدم بشكلٍ صريح: نمط كائني التوجه $format = "Y,m,d"; $time = "2009,2,26"; $date = DateTime::createFromFormat($format, $time); نمط إجرائي $format = "Y,m,d"; $time = "2009,2,26"; $date = date_create_from_format($format, $time); طباعة DateTime توفر PHP من الإصدار 4 وما بعده تابع تنسيق كائني التوجه يحول كائن DateTime إلى سلسلة نصية بالتنسيق الذي تريده: public string DateTime::format ( string $format ) تأخذ الدالة date() معاملًا واحدًا format بشكل سلسلة نصية ويستخدم محارف مفردة لتعريف التنسيق: Y: أربعة أرقام تمثل السنة (2016 مثلًا). y: رقمين يمثلان السنة (16 مثلًا). m: يمثّل الشهر بتنسيق رقم (من 01 إلى 12). M: يمثّل الشهر بثلاثة أحرف (مثل …Jan, Feb, Mar). j: يمثّل يوم في الشهر دون أصفار بادئة (من 1 إلى 31). D: يمثّل يوم في الأسبوع بثلاثة أحرف (مثل …Mon, Tue, Wed). h: يمثّل الساعة بتنسيق 12-ساعة (01 إلى 12). H: يمثّل الساعة بتنسيق 24-ساعة (00 إلى 23). A: إما AM أو PM. i: يمثّل الدقائق دون أصفار بادئة (من 00 إلى 59). s: يمثّل الثواني دون أصفار بادئة (من 00 إلى 59). يمكنك الاطلاع على اللائحة الكاملة هنا. الاستخدام يمكن استخدام هذه المحارف في تركيبات مختلفة لعرض الوقت بأي تنسيق تريده، إليك بعض الأمثلة: $date = new DateTime('2000-05-26T13:30:20'); /* Friday, May 26, 2000 at 1:30:20 PM */ $date->format("H:i"); /* 13:30 */ $date->format("H i s"); /* 13 30 20 */ $date->format("h:i:s A"); /* 01:30:20 PM */ $date->format("j/m/Y"); /* 26/05/2000 */ $date->format("D, M j 'y - h:i A"); /* Fri, May 26 '00 - 01:30 PM */ التنسيق الكائني التوجه: $date->format($format) التنسيق الإجرائي المشابه: date_format($date, $format) الحصول على الفرق بين تاريخين/وقتين يعدّ استخدام الصنف DateTime الطريقة الأكثر عمليّة لنحصل على الفرق بين تاريخين أو وقتين، مثال: <?php // DateTime إنشاء كائن من الصنف $twoYearsAgo = new DateTime("2014-01-18 20:05:56"); // DateTime إنشاء كائن ثاني من الصنف $now = new DateTime("2016-07-21 02:55:07"); // حساب الفرق $diff = $now->diff($twoYearsAgo); // فرق السنوات بين التاريخين $diff->y يحتوي $yearsDiff = $diff->y; // فرق الدقائق بين التاريخين $diff->m يحتوي $monthsDiff = $diff->m; // فرق الأيام بين التاريخين $diff->d يحتوي $daysDiff = $diff->d; // فرق الساعات بين التاريخين $diff->h يحتوي $hoursDiff = $diff->h; // فرق الدقائق بين التاريخين $diff->i يحتوي $minsDiff = $diff->i; // فرق الثواني بين التاريخين $diff->s يحتوي $secondsDiff = $diff->s; // فرق الأيام بين التاريخين $diff->days يحتوي $totalDaysDiff = $diff->days; // $diff استخلاص معلومات المتغير var_dump($diff); يمكننا أيضًا استخدام عوامل الموازنة للموازنة بين تاريخين بشكلٍ أسهل: <?php // DateTime إنشاء كائن من الصنف $twoYearsAgo = new DateTime("2014-01-18 20:05:56"); // DateTime إنشاء كائن ثاني من الصنف $now = new DateTime("2016-07-21 02:55:07"); var_dump($now > $twoYearsAgo); // bool(true) var_dump($twoYearsAgo > $now); // bool(false) var_dump($twoYearsAgo <= $twoYearsAgo); // bool(true) var_dump($now == $now); // bool(true) تغيير التاريخ إلى تنسيق آخر أسهل طريقة لتغيير تنسيق التاريخ إلى تنسيق آخر هي استخدام strtotime() مع date()، إذ أنّ strtotime() ستغير التاريخ إلى تنسيق الختم الزمني unix ثمّ يُمرَّر إلى date() لتغييره إلى تنسيق جديد. $timestamp = strtotime('2008-07-01T22:35:17.02'); $new_date_format = date('Y-m-d H:i:s', $timestamp); يمكن كتابة الشيفرة السابقة بسطرٍ واحد: $new_date_format = date('Y-m-d H:i:s', strtotime('2008-07-01T22:35:17.02')); تذكّر أنّ الدالة strtotime() تحتاج أن يكون التاريخ بتنسيقٍ صالح وإلا ستُرجع false وتصبح قيمة التاريخ 1969-12-31. استخدام DateTime() توفر PHP بدءًا من الإصدار 5.2 الصنف DateTime() الذي يمنح أدوات قوية للعمل مع التاريخ والوقت، يمكننا استخدامه لكتابة الشيفرة السابقة كالتالي: $date = new DateTime('2008-07-01T22:35:17.02'); $new_date_format = $date->format('Y-m-d H:i:s'); العمل مع الأختام الزمنية unix تأخذ الدالة date() معاملها الثاني بصيغة ختم زمني unix وتُرجع تاريخ منسّق: $new_date_format = date('Y-m-d H:i:s', '1234567890'); يعمل DateTime() مع الختم الزمني unix بإضافة @ قبل الختم الزمني: $date = new DateTime('@1234567890'); $new_date_format = $date->format('Y-m-d H:i:s'); إذا كان الختم الزمني لديك مقدّر بأجزاء الثانية (قد تكون نهايته 000 و/أو يتكون من 13 محرف) ستحتاج أن تحوّله إلى الثواني قبل أن تستطيع تغيير تنسيقه، يوجد طريقتين للقيام بذلك: حذف آخر 3 أرقام باستخدام substr(): يمكن حذف الأرقام الثلاثة الأخيرة بعدة طرق لكن أسهلها استخدام substr(): $timestamp = substr('1234567899000', -3); القسمة على 1000: يمكنك أيضًا تحويل الختم الزمني إلى الثواني عن طريق القسمة على 1000، يمكنك استخدام المكتبة BCMath للقيام بالعمليات الرياضية كسلاسل نصية لأنّ الختم الزمني كبير جدًا على أنظمة 32 بت: $timestamp = bcdiv('1234567899000', '1000'); يمكنك استخدام strtotime() للحصول على ختم زمني unix: $timestamp = strtotime('1973-04-18'); يمكنك استخدام DateTime::getTimestamp() مع DateTime(): $date = new DateTime('2008-07-01T22:35:17.02'); $timestamp = $date->getTimestamp(); يمكنك بدلًا من ذلك استخدام خيار التنسيق U إذا كنت تستخدم الإصدار PHP 5.2: $date = new DateTime('2008-07-01T22:35:17.02'); $timestamp = $date->format('U'); العمل مع تنسيق التاريخ الغامض وغير المعياري لسوء الحظ ليست كل التواريخ التي يحتاج المطور أن يتعامل معها تكون منسّقة بطريقة معيارية، لكن لحسن الحظ وفرت PHP 5.3 حلًّا لذلك، تسمح لنا DateTime::createFromFormat() بأن نخبر PHP عن تنسيق التاريخ لذا يمكن تحليلها بنجاح إلى كائن DateTime لمزيد من المعالجة. $date = DateTime::createFromFormat('F-d-Y h:i A', 'April-18-1973 9:48 AM'); $new_date_format = $date->format('Y-m-d H:i:s'); أعطتنا PHP 5.4 قابلة الوصول إلى عنصر في الصنف عند إنشاء نسخة مما يسمح لنا أن نكتب الشيفرة السابقة في سطر واحد: $new_date_format = (new DateTime('2008-07-01T22:35:17.02'))->format('Y-m-d H:i:s'); لكن هذا لا يعمل مع DateTime::createFromFormat() بعد. تحليل وصف التاريخ باللغة الانكليزية إلى تنسيق تاريخ يمكن تحليل نصوص مختلفة باللغة الانكليزية إلى تواريخ باستخدام الدالتين strtotime() وdate() معًا. // طباعة التاريخ الحالي echo date("m/d/Y", strtotime("now")), "\n"; // m/d/Y طباعة تاريخ 10 أيلول، 2000 بالتنسيق echo date("m/d/Y", strtotime("10 September 2000")), "\n"; // طباعة تاريخ الأمس echo date("m/d/Y", strtotime("-1 day")), "\n"; // طباعة نتيجة تاريخ اليوم + أسبوع echo date("m/d/Y", strtotime("+1 week")), "\n"; // طباعة نتيجة تاريخ اليوم + أسبوع ويومين و4 ساعات وثانيتين echo date("m/d/Y", strtotime("+1 week 2 days 4 hours 2 seconds")), "\n"; // طباعة تاريخ يوم الخميس القادم echo date("m/d/Y", strtotime("next Thursday")), "\n"; // طباعة تاريخ الاثنين الماضي echo date("m/d/Y", strtotime("last Monday")), "\n"; // طباعة تاريخ اليوم الأول من الشهر القادم echo date("m/d/Y", strtotime("First day of next month")), "\n"; // طباعة تاريخ اليوم الأخير من الشهر القادم echo date("m/d/Y", strtotime("Last day of next month")), "\n"; // طباعة تاريخ اليوم الأول من الشهر الماضي echo date("m/d/Y", strtotime("First day of last month")), "\n"; // طباعة تاريخ اليوم الأخير من الشهر الماضي echo date("m/d/Y", strtotime("Last day of last month")), "\n"; استخدام ثوابت معرّفة مسبقًا لتنسيق التاريخ بدءًا من الإصدار PHP 5.1.0 يمكننا استخدام ثوابت معرّفة مسبقًا لتنسيق التاريخ في الدالة date() بدلًا من السلاسل النصية. ثوابت تنسيق التاريخ المعرّفة مسبقًا المتاحة: DATE_ATOM // (2016-07-22T14:50:01+00:00) (ATOM) الذرة DATE_COOKIE // (Friday, 22-Jul-16 14:50:01 UTC) HTTP ملفات تعريف الارتباط DATE_RSS // (Fri, 22 Jul 2016 14:50:01 +0000) RSS DATE_W3C // (2016-07-22T14:50:01+00:00) اتحاد شبكة الويب العالمية DATE_ISO8601 // (2016-07-22T14:50:01+0000) ISO-8601 DATE_RFC822 // (Fri, 22 Jul 16 14:50:01 +0000) RFC 822 DATE_RFC850 // (Friday, 22-Jul-16 14:50:01 UTC) RFC 850 DATE_RFC1036 // (Fri, 22 Jul 16 14:50:01 +0000) RFC 1036 DATE_RFC1123 // (Fri, 22 Jul 2016 14:50:01 +0000) RFC 1123 DATE_RFC2822 // (Fri, 22 Jul 2016 14:50:01 +0000) RFC 2822 DATE_RFC3339 // (2016-07-22T14:50:01+00:00) DATE_ATOM نفس أمثلة: echo date(DATE_RFC822); // Fri, 22 Jul 16 14:50:01 +0000 echo date(DATE_ATOM,mktime(0,0,0,8,15,1947)); // 1947-08-15T00:00:00+05:30 ترجمة -وبتصرف- للفصول [Datetime Class - Working with Dates and Time] من كتاب PHP Notes for Professionals book اقرأ أيضًا المقال التالي: بنى التحكم والحلقات التكرارية في PHP المقال السابق: التنفيذ على المصفوفات والتعامل معها في PHP تعلم PHP1 نقطة
-
السلام عليكم . ادرس في دورة تطوير واجهات المستخدم قسم صفحات الهبوط انشات صفحة لمطار و لكن اواجه مشكة في ظهور قسم carousel slide airplane.zip1 نقطة
-
في ملف Scripts.js قمت بتهيئة الـ Carousel، ثم مرة أخرى في ملف HTML من خلال السمة data-bs-ride="carousel" في العنصر <div id="testimonialCarousel"> والتي تخبر Bootstrap ببدء تشغيل الـ Carousel تلقائيًا عند تحميل الصفحة. لذا قم بحذف الكود الذي في ملف Scripts.js وسيتم حل المشكلة. ولديك أخطاء أخرى، حيث قمت بتضمين JQuery ولا حاجة إلى ذلك مع الإصدار 5 من بوتستراب فهو لا يعتمد على تلك المكتبة بل جافاسكريبت فقط، وكذلك قمت بتضمين bootstrap.rtl.min.css يجب حذفه وإبقاء bootstrap.min.css فقط لتجنب التعارض.1 نقطة
-
أولاً لا يفضل وضع جميع المشاريع في مستودع واحد ويفضل فصل المشاريع في مستودعات مستقلة حيث يسهل عرض كل مشروع بشكل مستقل على GitHub. يجعل كل مشروع قابلاً للمشاركة والنشر بصورة منفصلة. يسهل توثيقك للمشروع في سيرتك الذاتية أو عند التقديم على فرص عمل ويفضل أيضاً حالياً في هذه الفترة أي دراسة الدورة فيفضل وضع كل مشروع في مستودع مستقل وبالنسبة للتمارين البسيطة فيمكن وضعهل في مستودع واحد وبالنسبة لحذف المجلد فيمكنك حذف المجلد بواسطة git أولاً كالتالي git rm -r GitTutorProject/img ثم بعدها دفع (push) التغييرات إلى المستودع البعيد1 نقطة
-
قواعد صنف الاختبار بفرض لدينا الصنف LoginForm مع التابع rules()، والذي يستخدم في صفحة تسجيل الدخول مثل قالب لإطار العمل: class LoginForm { public $email; public $rememberMe; public $password; // (1) public function rules() { return [ // البريد الإلكتروني وكلمة السر مطلوبان [['email', 'password'], 'required'], // يجب أن يكون البريد الإلكتروني بصياغة بريد إلكتروني ['email', 'email'], // يجب أن يكون الحقل rememberMe قيمة منطقية ['rememberMe', 'boolean'], // يجب أن تطابق كلمة السر هذا النمط (أي تحوي أحرف وأرقام فقط) ['password', 'match', 'pattern' => '/^[a-z0-9]+$/i'], ]; } // تتحقق هذه الدالة من صحة القواعد الممررة public function validate($rule) { $success = true; list($var, $type) = $rule; foreach ((array) $var as $var) { switch ($type) { case "required": $success = $success && $this->$var != ""; break; case "email": $success = $success && filter_var($this->$var, FILTER_VALIDATE_EMAIL); break; case "boolean": $success = $success && filter_var($this->$var, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null; break; case "match": $success = $success && preg_match($rule["pattern"], $this->$var); break; default: throw new \InvalidArgumentException("Invalid filter type passed") } } return $success; } } في الموضع (1) يعيد التابع rules() مصفوفة بمتطلبات كل حقل، يستخدم نموذج تسجيل الدخول البريد الإلكتروني وكلمة المرور لاستيثاق المستخدم. نستخدم اختبار الوحدات لإجراء الاختبارات على هذا الصنف، أي للتحقق من الشيفرة المصدرية لمعرفة إذا كانت تناسب توقعاتنا: class LoginFormTest extends TestCase { protected $loginForm; // تنفيذ الشيفرة في بداية الاختبار public function setUp() { $this->loginForm = new LoginForm; } // (1) public function testRuleValidation() { $rules = $this->loginForm->rules(); // التهيئة للتحقق من صحة واختبار البيانات التالية $this->loginForm->email = "valid@email.com"; $this->loginForm->password = "password"; $this->loginForm->rememberMe = true; $this->assertTrue($this->loginForm->validate($rules), "Should be valid as nothing is invalid"); // اختبار صحة البريد الإلكتروني // بما أننا حددنا أن يكون البريد الإلكتروني بصياغة بريد إلكتروني فلا يمكن أن يكون فارغًا $this->loginForm->email = ''; $this->assertFalse($this->loginForm->validate($rules), "Email should not be valid (empty)"); // لا يحتوي البريد الإلكتروني على العلامة "@" لذا فهو غير صحيح $this->loginForm->email = 'invalid.email.com'; $this->assertFalse($this->loginForm->validate($rules), "Email should not be valid (invalid format)"); // قيمة صحيحة للبريد الإلكتروني من أجل الاختبار التالي $this->loginForm->email = 'valid@email.com'; // اختبار صحة كلمة المرور والتي يجب ألا تكون فارغة (بما أنها مطلوبة) $this->loginForm->password = ''; $this->assertFalse($this->loginForm->validate($rules), "Password should not be valid (empty)"); // قيمة صحيحة لكلمة المرور من أجل الاختبار التالي $this->loginForm->password = 'ThisIsMyPassword'; // اختبار صحة الحقل rememberMe $this->loginForm->rememberMe = 999; $this->assertFalse($this->loginForm->validate($rules), "RememberMe should not be valid (integer type)"); // قيمة صحيحة للحقل rememberMe من أجل الاختبار التالي $this->loginForm->rememberMe = true; } } في الموضع (1) يجب أن نستخدم التابع validate() للتحقق من صحة قواعدنا، يعود التابع testRuleValidation() إلى اختبار الوحدة الخاص بالصنف LoginFormTest ويختبر القواعد المذكورة سابقًا. كيف يمكن أن يساعد اختبار الوحدات هنا (باستثناء الأمثلة العامة)؟ يناسبنا عند الحصول على نتائج غير متوقعة مثلًا، بفرض لدينا هذه القاعدة: ['password', 'match', 'pattern' => '/^[a-z0-9]+$/i'], إذا نسينا شيئًا واحدًا مهمًا وكتبنا: ['password', 'match', 'pattern' => '/^[a-z0-9]$/i'], من الصعب اكتشاف الأخطاء مع وجود عشرات القواعد المختلفة، وبفرض أننا لا نستخدم البريد الإلكتروني وكلمة المرور فقط، إليك اختبار الوحدة هذا: // التهيئة للتحقق من صحة واختبار البيانات التالية $this->loginForm->email = "valid@email.com"; $this->loginForm->password = "password"; $this->loginForm->rememberMe = true; $this->assertTrue($this->loginForm->validate($rules), "Should be valid as nothing is invalid"); سيمرر هذا الاختبار مثالنا الأول وليس الثاني لأنه لدينا خطأ مطبعي في المثال الثاني (لم نكتب علامة +)، مما يعني أنه سيقبل حرف/رقم واحد فقط. يمكن تنفيذ اختبار الوحدات في الطرفية باستخدام الأمر phpunit [path_to_file]، إذا كان كل شيء صحيحًا، يجب أن نكون قادرين على رؤية الحالة OK لكل الاختبارات وإلا سنرى إما Error لخطأ في الصيغة، أو Fail على الأقل لسطر واحد لم يُمرَّر في ذلك التابع. يمكننا أيضًا باستخدام معاملات إضافية مثل --coverage أن نحصل على عدد الأسطر المُختبرة من الشيفرة في الواجهة الخلفية backend وأيها نجحَ/فشلَ، يُطبَّق هذا على أي إطار عمل ثبّتَ PHPUnit. إليك مثال عن طريقة ظهور اختبار PHPUnit في الطرفية، وهو مثال عام وليس له صلة بمثالنا: مقدمو بيانات PHPUnit تحتاج توابع الاختبار عادةً إلى بيانات لتُختَبر بها، ولاختبار بعض التوابع كاملةً تحتاج لتوفير مجموعات بيانات مختلفة لكل حالة اختبار ممكنة، يمكنك القيام بذلك يدويًا باستخدام الحلقات، مثال: ... public function testSomething() { $data = [...]; foreach($data as $dataSet) { $this->assertSomething($dataSet); } } ... يمكن أن تجد هذه الطريقة مريحة ولكن لها بعض العيوب، أولًا عليك أن تؤدي إجراءات إضافية لاستخراج البيانات إذا كانت دالة الاختبار تقبل عدة معاملات، ثانيًا سيكون من الصعب في حالة الفشل تمييز مجموعة البيانات الخاطئة بدون تنقيح أخطاء ورسائل إضافية، ثالثًا يوفر PHPUnit طريقة تلقائية للتعامل مع مجموعات بيانات الاختبار باستخدام مقدمي البيانات. مقدم البيانات دالة يجب أن ترجع بيانات حالة الاختبار الخاصة بك، ويجب أن تكون هذه الدالة عامة وترجع إما مصفوفة من المصفوفات أو كائن ينفّذ الواجهة Iterator ويُرجع مصفوفة لكل خطوة تكرارية. كل مصفوفة جزء من المجموعة collection التي سيستدعيها تابع الاختبار مع محتويات المصفوفة كوسائط لها. لاستخدام مقدم البيانات مع الاختبار نستخدم التوصيف @dataProvider مع اسم دالة مقدم البيانات المحددة: /** * @dataProvider dataProviderForTest */ public function testEquals($a, $b) { $this->assertEquals($a, $b); } public function dataProviderForTest() { return [ [1,1], [2,2], [3,2] //this will fail ]; } مصفوفة مصفوفات لاحظ أنّ الدالة dataProviderForTest() ترجع مصفوفة من مصفوفات، كل مصفوفة متداخلة تحتوي عنصرين سيملآن المعاملات الضرورية للدالة testEquals()، إذا لم يكن هناك عناصر كافية سيُرمى خطأ مشابه للخطأ التالي: Missing argument 2 for Test::testEquals() حيث سيمر PHPUnit تلقائيًا على كل البيانات وينفذ الاختبارات، كما هو ظاهر فيما يأتي: public function dataProviderForTest() { return [ [1,1], // [0] testEquals($a = 1, $b = 1) [2,2], // [1] testEquals($a = 2, $b = 2) [3,2] // [2] There was 1 failure: 1) Test::testEquals with data set #2 (3, 4) ]; } يمكن أن تُسمَّى كل مجموعة بيانات ليكون من الأسهل اكتشاف بيانات الفشل: public function dataProviderForTest() { return [ 'Test 1' => [1,1], // [0] testEquals($a = 1, $b = 1) 'Test 2' => [2,2], // [1] testEquals($a = 2, $b = 2) 'Test 3' => [3,2] // [2] There was 1 failure: // 1) Test::testEquals with data set "Test 3" (3, 4) ]; } المكررات class MyIterator implements Iterator { protected $array = []; public function __construct($array) { $this->array = $array; } function rewind() { return reset($this->array); } function current() { return current($this->array); } function key() { return key($this->array); } function next() { return next($this->array); } function valid() { return key($this->array) !== null; } } ... class Test extends TestCase { /** * @dataProvider dataProviderForTest */ public function testEquals($a) { $toCompare = 0; $this->assertEquals($a, $toCompare); } public function dataProviderForTest() { return new MyIterator([ 'Test 1' => [0], 'Test 2' => [false], 'Test 3' => [null] ]); } } كما تلاحظ، فإنَّ المكرِّر البسيط يعمل أيضًا، وأنّ مقدم البيانات يجب أن يرجع مصفوفة $parameter حتى من أجل معامل واحد. إذا غيّرنا التابع current() الذي يعيد البيانات في كل تكرار، بالشكل التالي: function current() { return current($this->array)[0]; } أو غيّرنا البيانات الفعلية: return new MyIterator([ 'Test 1' => 0, 'Test 2' => false, 'Test 3' => null ]); سنحصل على خطأ: There was 1 warning: 1) Warning The data provider specified for Test::testEquals is invalid. من غير المفيد طبعًا استخدام الكائن Iterator لتكرار محتويات مصفوفة بسيطة، يجب أن ينفّذ بعض المنطق المحدد لحالتك. المولدات generators لم يُشار إليها بشكلٍ صريح في التوثيق الرسمي لكن يمكنك استخدامها كمقدم بيانات أيضًا، لاحظ أنّ الصنف Generator ينفّذ الواجهة Iterator فعليًا، إليك مثال عن استخدام DirectoryIterator مع مولِّد: /** * @param string $file * * @dataProvider fileDataProvider */ public function testSomethingWithFiles($fileName) { // $fileName متاح هنا // اختبر هنا } public function fileDataProvider() { $directory = new DirectoryIterator('path-to-the-directory'); foreach ($directory as $file) { if ($file->isFile() && $file->isReadable()) { // تنفيذ المولِّد هنا yield [$file->getPathname()]; } } } لاحظ أنّ مقدم البيانات يُرجع مصفوفة، وستحصل على تحذير أنّ مقدم البيانات غير صحيح. استثناءات الاختبار لنفرض أنك تريد اختبار تابع يرمي استثناءً. class Car { /** * @throws \Exception */ public function drive() { throw new \Exception('Useful message', 1); } } يمكنك القيام بذلك عن طريق تضمين استدعاء التابع في كتلة try/catch وإجراء توكيدات على خاصيات كائن الاستثناء، أو يمكنك استخدام توابع توكيد الاستثناء للمزيد من الملاءمة، يمكنك بدءًا من الإصدار PHPUnit 5.2 استخدام توابع expectX() المتاحة لتأكيد نوع ورسالة وشيفرة الاستثناء. class DriveTest extends PHPUnit_Framework_TestCase { public function testDrive() { // التحضير $car = new \Car(); $expectedClass = \Exception::class; $expectedMessage = 'Useful message'; $expectedCode = 1; // الاختبار $this->expectException($expectedClass); $this->expectMessage($expectedMessage); $this->expectCode($expectedCode); // التنفيذ $car->drive(); } } يمكنك أن تستخدم التابع setExpectedException بدلًا من expectX() إذا كنت تستخدم إصدارًا قديمًا من PHPUnit لكن تذكر أنه تابع مُهمل وسيُحذف في الإصدار 6. class DriveTest extends PHPUnit_Framework_TestCase { public function testDrive() { // التحضير $car = new \Car(); $expectedClass = \Exception::class; $expectedMessage = 'Useful message'; $expectedCode = 1; // الاختبار $this->setExpectedException($expectedClass, $expectedMessage, $expectedCode); // التنفيذ $car->drive(); } } الأداء التحليل مع Xdebug تُعَدّ إضافة Xdebug متاحةً للمساعدة في تحليل تطبيقات PHP، بالإضافة إلى تنقيح الأخطاء وقت التنفيذ، عند تنفيذ المحلل يُكتب الخرج في ملف بصياغة ثنائية تدعى cachegrind. توجد تطبيقات متوفرة على كل منصة لتحليل هذه الملفات. لتمكين التحليل نثبّت الإضافة ونعدّل إعدادات ملف php.ini. سننفذ في مثالنا المحلل اختياريًا بالاعتماد على معامل الطلب، يسمح لنا هذا بالحفاظ على الإعدادات ثابتة وتشغيل المحلل عندما نحتاج فقط. // اضبطه إلى 1 لتشغيله عند كل طلب xdebug.profiler_enable = 0 // لنستخدم معامل GET/POST لتشغيل المحلِّل xdebug.profiler_enable_trigger = 1 // قيمة GET/POST التي سنمررها، فارغة من أجل أي قيمة xdebug.profiler_enable_trigger_value = "" // عرض ملفات الذاكرة cachegrind في المسار /tmp حتى ينظفها النظام لاحقًا xdebug.profiler_output_dir = "/tmp" xdebug.profiler_output_name = "cachegrind.out.%p" ثم استخدم عميل ويب يرسل طلبًا إلى رابط التطبيق الذي ترغب بتحليله، مثل: http://example.com/article/1?XDEBUG_PROFILE=1 أثناء معالجة الصفحة للطلب سيكتب في ملف له اسم مشابه للتالي: /tmp/cachegrind.out.12345 لاحظ أنّه سيكتب ملف واحد لكل عملية/طلب PHP يُنفَّذ، لذا إذا أردت تحليل نموذج مُرسل بالطريقة POST سيُكتب تحليل واحد من أجل الطريقة GET لعرض نموذج HTML وستحتاج إلى تمرير المعامل XDEBUG_PROFILE ، من أجل الإقدام على طلب POST اللاحق لتحليل الطلب الثاني الذي يعالج النموذج، لذا فقد يكون من الأسهل تنفيذ مكتبة curl لإرسال نموذج بالطريقة POST مباشرةً عند التحليل. بمجرد أن يُكتب التحليل، فيمكنك قراءة الذاكرة المخبئية cache بتطبيق مثل KCachegrind: سيعرض التطبيق معلومات التحليل متضمنةً: الدوال المنفَّذة وقت استدعاء الدالة بمفردها واستدعاءات الدالة اللاحقة. عدد مرات استدعاء كل دالة. رسوم بيانية للاستدعاء روابط للشيفرة المصدرية من الواضح أنّ ضبط الأداء خاص جدًا بحالات استخدام كل تطبيق، بشكل عام من الأفضل التركيز على النقاط التالية: يجب ألا ترى استدعاءات متكررة لنفس الدالة، بالنسبة للدوال التي تعالج البيانات وتستعلم عنها قد يكون هناك فرص للتخزين المؤقت. وجود دوال بطيئة التنفيذ، أين يستهلك التطبيق معظم وقته؟ أفضل عائد لضبط الأداء هو التركيز على أجزاء التطبيق التي تستهلك معظم وقته. ملاحظة: إنّ إضافة Xdebug وخاصةً ميزاتها التحليلية مكثّفة للموارد وتبطئ تنفيذ PHP، لذا يُنصح بعدم تنفيذها في بيئة خادم الإنتاج. استخدام الذاكرة يُضبط حد ذاكرة زمن تنفيذ PHP باستخدام موجّه INI الـ memory_limit، حيث يمنع هذا الضبط أي تنفيذ PHP مفرد من استخدام الكثير من الذاكرة مما يؤدي إلى استنزافها من أجل السكربتات الأخرى وبرنامج النظام. حد الذاكرة الافتراضي هو 128MB ويمكن أن يتغير في ملف php.ini أو في وقت التنفيذ. يمكن أن يُضبط ليكون غير محدود لكن يعدّ هذا عمومًا ممارسةً سيئة. يمكن أن يُحدَّد الاستخدام الدقيق للذاكرة أثناء وقت التنفيذ عن طريق استدعاء الدالة memory_get_usage() التي تعيد عدد بايتات الذاكرة المحجوزة للسكربت الحالي المُنفَّذ. بدءًا من الإصدار PHP 5.2 يوجد لهذه الدالة معامل منطقي اختياري للحصول على ذاكرة النظام المحجوزة الكلية على عكس الذاكرة الفعالة التي تستخدمها PHP. <?php echo memory_get_usage() . "\n"; // الخرج 350688 (أو شيء ما مشابه وهذا يعتمد على النظام وإصدار PHP) // لنستهلك جزءًا من الذاكرة RAM $array = array_fill(0, 1000, 'abc'); echo memory_get_usage() . "\n"; // 387704 // حذف المصفوفة من الذاكرة unset($array); echo memory_get_usage() . "\n"; // 350784 تعطيك الآن الدالة memory_get_usage استخدام الذاكرة في الوقت الذي نُفِّذت فيه، قد تخصص وتلغي تخصيص الكثير من الذاكرة بين استدعاءات الدالة المتلاحقة، يمكنك استخدام الدالة memory_get_peak_usage() للحصول على الحجم الأعظمي من الذاكرة المستخدمة حتى نقطة معينة. <?php echo memory_get_peak_usage() . "\n"; // 385688 $array = array_fill(0, 1000, 'abc'); echo memory_get_peak_usage() . "\n"; // 422736 unset($array); echo memory_get_peak_usage() . "\n"; // 422776 لاحظ أنّ القيمة إما سترتفع أو تبقى ثابتة. التحليل باستخدام XHProf XHProf محلل PHP مكتوب من قِبل شركة فيسبوك لتوفير بديل أخف للإضافة XDebug، يمكن تمكين/تعطيل التحليل بعد تثبيت الوحدة xhprof من شيفرة PHP: xhprof_enable(); doSlowOperation(); $profile_data = xhprof_disable(); ستحتوي المصفوفة المُرجعة على بيانات حول عدد الاستدعاءات ووقت المعالج واستخدام الذاكرة لكل دالة تم الوصول إليها من داخل doSlowOperation(). يمكن استخدام الدالة الموالية على أساس خيار أخف لتسجيل معلومات التحليل لجزء من الطلبات فقط (وبصياغة مختلفة). xhprof_sample_enable()/xhprof_sample_disable() ولهذا يستخدم المحلل بعض الدوال المساعدة (معظمها غير موثَّق) لعرض البيانات (اطلع على هذا المثال)، أو يمكنك أاستعمال أدوات أخرى لتصورها (يمكنك الاطلاع على هذا المثال من مدونة platform.sh). ترجمة -وبتصرف- للفصول Unit Testing - Performance من كتاب PHP Notes for Professionals book1 نقطة
-
التشفير وفك التشفير المتناظر لملفات كبيرة باستخدام OpenSSL لا توفر PHP دالة مضمنة لتشفير وفك تشفير الملفات الكبيرة، يمكن استخدام الدالة openssl_encrypt لتشفير السلاسل النصية لكن يعد تحميل ملف كبير جدًا في الذاكرة فكرةً سيئةً، لذا يجب كتابة دالة تقوم بهذا العمل، يستخدم هذا المثال خوارزمية AES-128-CBC المتناظرة لتشفير أجزاء صغيرة من ملف كبير وكتابتها في ملف آخر. تشفير الملفات // (1) define('FILE_ENCRYPTION_BLOCKS', 10000); /** * تشفير الملف الممرر وحفظ النتيجة في ملف جديد باللاحقة ".enc" * * @param string $source مسار الملف الذي نريد تشفيره * @param string $key المفتاح المستخدم للتشفير * @param string $dest اسم الملف الذي نريد أن نكتب فيه الملف المشفَّر * @return string|false * تعيد هذه الدالة اسم الملف المنشأ أو FALSE إذا حدث خطأ */ function encryptFile($source, $key, $dest) { $key = substr(sha1($key, true), 0, 16); $iv = openssl_random_pseudo_bytes(16); $error = false; if ($fpOut = fopen($dest, 'w')) { // ضع شعاع التهيئة في بداية الملف fwrite($fpOut, $iv); if ($fpIn = fopen($source, 'rb')) { while (!feof($fpIn)) { $plaintext = fread($fpIn, 16 * FILE_ENCRYPTION_BLOCKS); $ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA,$iv); // استخدم أول 16 بايت من النص المشفر كشعاع التهيئة التالي $iv = substr($ciphertext, 0, 16); fwrite($fpOut, $ciphertext); } fclose($fpIn); } else { $error = true; } fclose($fpOut); } else { $error = true; } return $error ? false : $dest; } نحدد في الموضع (1) عدد الكتل التي يجب قراءتها من الملف المصدري من أجل كل جزء، بحيث تتألف كل كتلة من 16 بايت من أجل الخوارزمية الآتية: 'AES-128-CBC' لهذا، فإذا قرأنا 10000 كتلة نحمّل 160 كيلوبايت في الذاكرة، يمكنك تعديل هذه القيمة لقراءة/كتابة أجزاء أصغر/أكبر. فك تشفير الملفات يمكنك استخدام هذه الدالة لفك تشفير الملفات المشفرة بالدالة السابقة. /** * فك تشفير الملف الممرر وحفظ النتيجة في ملف جديد مع حذف آخر 4 محارف من اسم الملف * @param string $source مسار الملف الذي نريد فك تشفيره * @param string $key المفتاح المستخدم لفك التشفير (ويجب أن يكون نفس المفتاح المستخدم للتشفير) * @param string $dest اسم الملف حيث يجب أن نكتب الملف الجديد بعد فك التشفير * @return string|false * تعيد هذه الدالة اسم الملف المنشأ أو FALSE إذا حدث خطأ */ function decryptFile($source, $key, $dest) { $key = substr(sha1($key, true), 0, 16); $error = false; if ($fpOut = fopen($dest, 'w')) { if ($fpIn = fopen($source, 'rb')) { // الحصول على شعاع التهيئة من بداية الملف $iv = fread($fpIn, 16); while (!feof($fpIn)) { $ciphertext = fread($fpIn, 16 * (FILE_ENCRYPTION_BLOCKS + 1)); // يجب أن نقرأ كتلة واحدة زيادة عن التشفير لفك التشفير $plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA,$iv); // استخدم أول 16 بايت من النص المشفر كشعاع التهيئة التالي $iv = substr($ciphertext, 0, 16); fwrite($fpOut, $plaintext); } fclose($fpIn); } else { $error = true; } fclose($fpOut); } else { $error = true; } return $error ? false : $dest; } طريقة الاستخدام إليك الشيفرة التالية لتعرف كيفية استخدام الدوال السابقة. $fileName = __DIR__.'/testfile.txt'; $key = 'my secret key'; file_put_contents($fileName, 'Hello World, here I am.'); encryptFile($fileName, $key, $fileName . '.enc'); decryptFile($fileName . '.enc', $key, $fileName . '.dec'); ستنشئ هذه الشيفرة ثلاثة ملفات: testfile.txt وفيه النص الأصلي. testfile.txt.enc فيه الملف المشفر. testfile.txt.dec فيه الملف بعد فك تشفيره ويجب أن يكون نفس محتويات الملف testfile.txt. التشفير المتناظر يوضح هذا المثال التشفير المتناظر باستخدام خوارزمية AES 256 بالنمط CBC وهو اختصار لـ Cipher Block Chaining، نحتاج شعاع تهيئة لذا نولّد واحدًا باستخدام دالة openssl، ويستخدم المتغير $strong لتحديد فيما إذا كان شعاع التهيئة المولَّد قويًا من ناحية التشفير. التشفير // طريقة التشفير $method = "aes-256-cbc"; // الحصول على طول شعاع التهيئة المطلوب $iv_length = openssl_cipher_iv_length($method); // ضبط للقيمة false من أجل السطر التالي $strong = false; // توليد شعاع التهيئة $iv = openssl_random_pseudo_bytes($iv_length, $strong); /* يحتاج شعاع التهيئة للاسترجاع لاحقًا لذا خزنه في قاعدة البيانات لكن لا تعيد استخدام نفس شعاع التهيئة لتشفير بيانات مرةً أخرى */ if(!$strong) { // رمي استثناء إذا لم يكن شعاع التهيئة قويًا من ناحية التشفير throw new Exception("IV not cryptographically strong!"); } // الرسالة السرية $data = "This is a message to be secured."; // كلمة المرور $pass = "Stack0verfl0w"; /* يجب أن تُرسل كلمة المرور بالطريقة POST عبر جلسة HTTPS، قمنا بتخزينها في متغير هنا لأغراض توضيحية * */ // التشفير $enc_data = openssl_encrypt($data, $method, $password, true, $iv); فك التشفير // استعادة شعاع التهيئة من قاعدة البيانات وكلمة المرور من الطلب POST // فك التشفير $dec_data = openssl_decrypt($enc_data, $method, $pass, true, $iv); التشفير وفك التشفير بالأساس 64 إذا كانت البيانات المشفرة تحتاج للإرسال أو التخزين في نص قابل للطباعة عندها يمكن استخدام الدالتين ()base64_encode، و ()base64_decode على الترتيب. // تشفير الترميز بالأساس 64 $enc_data = base64_encode(openssl_encrypt($data, $method, $password, true, $iv)); // فك الترميز وفك التشفير $dec_data = openssl_decrypt(base64_decode($enc_data), $method, $password, true, $iv); دوال تعمية كلمة المرور بما أنّ خدمات الويب الأكثر أمنًا تتجنب تخزين كلمات المرور بصياغة نص واضح فإنّ بعض اللغات مثل PHP توفر دوال تعميةصعبة الاستخراج hashing، ومتنوعة لدعم معيار الصناعة الأكثر أمنًا. يوفر هذا المثال توثيقًا لعمليات التعمية المناسب باستخدام PHP. إنشاء كلمة مرور معماة ننشئ نسخة معماة لكلمة المرور باستخدام الدالة password_hash() لاستخدام تعمية معيارية بأفضل ممارسة للصناعة الحالية أو لاشتقاق المفتاح، في وقت كتابة هذا النص المعيار هو bcrypt مما يعني أنّ PASSWORD_DEFAULT له نفس قيمة PASSWORD_BCRYPT. $options = [ 'cost' => 12, ]; $hashedPassword = password_hash($plaintextPassword, PASSWORD_DEFAULT, $options); المعامل الثالث ليس إجباريًا. يجب أن نختار القيمة 'cost' بالاعتماد على تجهيزات خادم الإنتاج، ستجعل زيادتها كلمة المرور أكثر تكلفةً عند توليدها، كلما زادت تكلفة استخراج وفك الكلمة المعماة، استغرق الأمر وقتًا أطول عند محاولة شخص ما استخراجها؛ ومثاليًا يجب أن تكون التكلفة أعلى ما يمكن لكن من الناحية العملية يجب ضبطها بحيث لا تؤدي إلى بطء شديد في كل شيء، من المناسب أن تكون بين 0.1 و0.4 ثانية، إذا كنت محتارًا استخدم القيمة الافتراضية. في الإصدارات السابقة للإصدار 5.5، الدوال password_* غير متوفرة، يجب أن تستخدم حزمة التوافق للحصول على بديل لهذه الدوال، لاحظ أنّ حزمة التوافق تتطلب الإصدار PHP 5.3.7 أو أعلى أو إصدار يحتوي على التصحيح $2y (مثل ريدهات). إذا لم تكن قادرًا على استخدامها فيمكنك تنفيذ عملية تعمية على كلمة المرور باستخدام الدالة crypt()، وبما أنّ password_hash() تُنفَّذ كغلاف حول الدالة crypt() فلن تحتاج لفقدان أي وظيفة. المثال التالي هو تنفيذ بسيط للتعمية بالمعيار bcrypt والتوافق مع password_hash() ومن غير المضمون أن يحافظ على نفس قوة تشفير التنفيذ الكامل للدالة password_hash(). إضافة غُفْل في عملية تعمية كلمة المرور على الرغم من موثوقية خوارزمية التشفير إلا أنّه ما يزال يوجد ثغرة تستهدف جداول قوس قزح ولذا ينصح باستخدام غُفْل salt، والغفل بالعربية هو شيء ما يُضاف لكلمة المرور قبل تعميتها لجعل السلسلة النصية المصدر فريدة (انظر كتاب «علم التعمية واستخراج المعمى عند العرب»)، بالنظر إلى كلمتي مرور متطابقتين فإنّ نتيجة التعمية لهما ستكون فريدة أيضًا لأن الأغْفَال المضافة إليها فريدة. يعد إضافة الأغفال العشوائية أحد أهم أجزاء أمان كلمة المرور الخاصة بك، وهذا يعني أنّه حتى مع جدول البحث lookup table لكلمات المرورة المعماة المعروفة فإنّ المهاجم لن يتمكن من مطابقة كلمة المرور المعماة الخاصة بالمستخدم مع كلمة المرور المعماة في قاعدة البيانات بسبب استخدام أغفال مختلفة، يجب أن تستخدم دائمًا أغفال عشوائية وقوية من ناحية التشفير اقرأ المزيد. باستخدام خوارزمية bcrypt ودالة password_hash() تُخزَّن أغفال النص الأصلي وأغفال النص المعمى الناتج مما يعني أنّه يمكن نقل النص المعمى عبر أنظمة ومنصات مختلفة وستبقى متطابقة مع كلمة المرور الأصلية. في الإصدارات السابقة للإصدار 7.0 يمكنك استخدام الخيار salt لتعريف الأغفال العشوائية الخاصة بك على الرغم من عدم التشجيع على هذا الإجراء. $options = [ 'salt' => $salt, ]; ملاحظة: إذا أهملت هذا الخيار ستولّد الدالة password_hash() غُفْلًا عشوائيًا لكل كلمة مرور مقطعة. بدءًا من الإصدار PHP 7.0.0 أُهمل خيار إضافة الغُفْل ومن المفضل الآن استخدام الغُفْل المولّد افتراضيًا. ترقية كلمة مرور معماة موجودة إلى خوارزمية أقوى إذا كنت تستخدم طريقة PASSWORD_DEFAULT لتجعل النظام يختار الخوارزمية الأفضل لتعمية كلمات المرور بها، مع زيادة قوة الخوارزمية الافتراضية قد ترغب في إعادة تعمية كلمات المرور القديمة عندما يسجل المستخدمون الدخول. <?php // حدد أولًا إذا كانت كلمة المرور الموفرة صحيحة if (password_verify($plaintextPassword, $hashedPassword)) { // حدد الآن إذا كانت النسخة المعماة الموجود قد أُنشئت بخوارزمية لم تعد افتراضية بعد الآن if (password_needs_rehash($hashedPassword, PASSWORD_DEFAULT)) { // أنشئ كلمة معماة جديدة مع الخوارزمية الافتراضية الجديدة $newHashedPassword = password_hash($plaintextPassword, PASSWORD_DEFAULT); // ثم احفظه في مخزن بياناتك // $db->update(...); } } ?> إذا لم تكن الدوال password_* متوفرةً في نظامك ولا تستطيع استخدام حزمة التوافق، فعندها يمكنك تحديد الخوارزمية واستخدامها لإنشاء التعمية الأصلية بطريقة مشابهة للتالي: <?php if (substr($hashedPassword, 0, 4) == '$2y$' && strlen($hashedPassword) == 60) { echo 'Algorithm is Bcrypt'; // يحدد "cost" مدى قوة إصدار Bcrypt preg_match('/\$2y\$(\d+)\$/', $hashedPassword, $matches); $cost = $matches[1]; echo 'Bcrypt cost is '.$cost; } ?> التحقق من كلمة المرور مقابل كلمة معماة توفر الدالة password_verify() المدمجة بدءًا من الإصدار PHP 5.5، إمكانية التحقق من صحة كلمة مرور مقابل كلمة معماة مقابلة لها، أو غير مقابلة. <?php if (password_verify($plaintextPassword, $hashedPassword)) { echo 'Valid Password'; } else { echo 'Invalid Password.'; } ?> تخزن كل خوارزميات عملية التعمية المدعومة معلومات تحدد آلية التعمية المستخدمة، لذا لا حاجة للإشارة إلى الخوارزمية المستخدمة لتعمية كلمة المرور الأصلية. إذا لم تكن الدوال password_* متوفرةً في نظامك ولا تستطيع استخدام حزمة التوافق، فيمكنك التحقق من كلمة المرور باستخدام الدالة crypt()، لاحظ أنه يجب اتخاذ احتياطات محددة لتجنب هجمات التوقيت. <?php // غير مضمون أن يحافظ على نفس قوة تشفير تنفيذ password_hash() الكامل if (CRYPT_BLOWFISH == 1) { // تتجاهل crypt() كل المحارف التي تتجاوز طول الغفل، لذا يمكننا تمرير ?كامل كلمة المرور المعماة $hashedCheck = crypt($plaintextPassword, $hashedPassword); // هذه موازنة وقت ثابت أساسية تعتمد على التنفيذ الكامل المستخدم في `password_hash()` $status = 0; for ($i=0; $i<strlen($hashedCheck); $i++) { $status |= (ord($hashedCheck[$i]) ^ ord($hashedPassword[$i])); } if ($status === 0) { echo 'Valid Password'; } else { echo 'Invalid Password'; } } ?> ترجمة -وبتصرف- للفصول Cryptography - Password Hashing Functions من كتاب PHP Notes for Professionals book1 نقطة
-
يُعَدّ Docker حاويةً شائعة جدًا تُستخدم على نطاقٍ واسع كحلّ لنشر الشيفرة في بيئات الإنتاج، كما أنها تسهّل إدارة وتوسيع تطبيقات الويب والخدمات الصغيرة. الحصول على صورة دوكر من أجل php لنشر التطبيق على دوكر نحتاج أولًا للحصول على الصورة من السجل registry. docker pull php سيوفر لك هذا أحدث إصدار للصورة من مستودع php الرسمي، تُستخدم php بشكلٍ عام لنشر تطبيقات الويب لذا نحتاج إلى خادم http ليتوافق مع الصورة. تأتي الصورة في الإصدار php:7.0 (أو إصدار أحدث) مُثبّتة مسبقًا مع apache لتنشر تطبيقك بدون مشاكل. كتابة dockerfile يُستخدم Dockerfile لضبط الصورة المخصصة التي سننشئها مع شيفرات تطبيق الويب، ننشئ ملف جديد Dockerfile في المجلد الجذر للمشروع ونضع فيه المحتويات التالية: FROM php:7.0-apache COPY /etc/php/php.ini /usr/local/etc/php/ COPY . /var/www/html/ EXPOSE 80 يستخدم السطر الأول لوصف الصورة التي يجب استخدامها لإنشاء صورة جديدة، يمكن تغيير هذا إلى أي إصدار PHP آخر محدد من السجل، والسطر الثاني لتحميل ملف php.ini إلى الصورة ويمكنك تغيير هذا الملف إلى موقع ملف مخصص آخر، وينسخ السطر الثالث الشيفرات في المجلد الحالي إلى /var/www/html والذي هو webroot بالنسبة لنا، تذكر أن /var/www/html داخل الصورة، أما السطر الأخير فسيفتح المنفذ 80 داخل حاوية دوكر. قد يكون لديك في بعض الحالات بعض الملفات التي لا تريدها على الخادم مثل ملف إعدادات البيئة، بفرض أنّ إعدادات البيئة موجودة لدينا في ملف .env ونريد تجاهله عندها نضيفه إلى .dockerignore في المجلد الجذر لشيفرتنا. بناء الصورة إنّ بناء الصورة شيء غير محدد في php، لكن لبناء الصورة التي تحدثنا عنها في الأعلى نستخدم مايلي: docker build -t <Image name> . يمكننا التحقق من بناء الصورة باستخدام: docker images سيعطيك هذا الأمر كل الصور المثبتة في نظامك. بدء حاوية التطبيق يمكننا البدء بتقديم الخدمة بمجرد أن تصبح الصورة جاهزة، نستخدم ما يلي لإنشاء حاوية من الصورة: docker run -p 80:80 -d <Image name> ستوجّه -p 80:80 في الأمر السابق المنفذ 80 الخاص بخادمك إلى المنفذ 80 الخاص بالحاوية، وستخبر الراية -d أنّه يجب تنفيذ الحاوية في الخلفية وتصف <Image name> الصورة التي يجب استخدامها لبناء الحاوية. التحقق من الحاوية نستخدم ما يلي للتحقق من الحاويات قيد التنفيذ: docker ps سيعطينا هذا الأمر قائمة بكل الحاويات التي تُنفَّذ. سجلات التطبيق تعدّ السجلات مهمة جدًا لتنقيح أخطاء التطبيق، وللتحقق منها نستخدم الأمر: docker logs <Container id> مخزن APCu APCu هو مخزن قيمة-مفتاح للذاكرة المشتركة في PHP، تُشارك الذاكرة بين عملياتPHP-FPM (أي Fast Process Manager) في نفس المجمع pool وتستمر البيانات المخزنة بين العمليات. تكرار محتويات المداخل يسمح الصنف APCUIterator بتكرار محتويات المداخل في المخزن المؤقت cache: foreach (new APCUIterator() as $entry) { print_r($entry); } يمكن تهيئة المكرِّر بتعبير نمطي اختياري لاختيار المداخل المتطابقة مع المفاتيح فقط: foreach (new APCUIterator($regex) as $entry) { print_r($entry); } يمكن الحصول على معلومات مدخل ذاكرة مؤقتة واحدة بالشكل التالي: $key = '…'; $regex = '(^' . preg_quote($key) . '$)'; print_r((new APCUIterator($regex))->current()); تخزين واسترجاع بسهولة يمكن استخدام apcu_store لتخزين قيم وapcu_fetch لاستعادتها: $key = 'Hello'; $value = 'World'; apcu_store($key, $value); print(apcu_fetch('Hello')); // 'World' تخزين معلومات توفر apcu_cache_info معلومات حول المخزن ومداخله: print_r(apcu_cache_info()); لاحظ أنّ استدعاء apcu_cache_info() بدون حد سيعيد كل البيانات المخزنة حاليًا، ولهذا نستخدم apcu_cache_info(true)، للحصول على البيانات الوصفية فقط، كما أنه من الأفضل استخدام الصنف APCUIterator للحصول على معلومات عن مداخل ذاكرة تخزين مؤقتة محددة. ترجمة -وبتصرف- للفصل Docker deployment - APCu من كتاب PHP Notes for Professionals book1 نقطة
-
خرج الصورة يمكن إنشاء صورة باستخدام دوال image* حيث * هي صيغة الملف، وهذه الدوال لها الصيغة المشتركة التالية: bool image___(resource $im [, mixed $to [ other parameters]] ) الحفظ إلى ملف يمكنك تمرير اسم الملف أو مجرى ملف مفتوح للمتغير $to إذا كنت تريد حفظ الصورة إلى ملف، إذا مررت مجرى فلا تحتاج لإغلاقه لأنّ مكتبة GD تغلقه تلقائيًا، مثلًا لحفظ ملف PNG: imagepng($image, "/path/to/target/file.png"); $stream = fopen("phar://path/to/target.phar/file.png", "wb"); imagepng($image2, $stream); // لا حاجة لإغلاق المجرى تأكد عند استخدام fopen من أنك تستخدم الراية b وليس الراية t لأن الملف هو خرج ثنائي، ولا تحاول أن تمرر fopen("php://temp", $f) أو fopen("php://memory", $f) لأنّ الدالة تُغلق المجرى بعد الاستدعاء ولن تبقى قادرًا على استدعائه أو استخدامه لاسترداد محتوياته مثلًا. الخرج كرد HTTP لا تحتاج إلى تمرير شيء (أو مرر null) كوسيط ثانٍ إذا كنت تريد أن ترجع هذه الصورة مباشرةً كرد للصورة (لإنشاء بطاقات ديناميكية مثلًا)، لكنك تحتاج إلى تحديد نوع المحتوى في رد HTTP: header("Content-Type: $mimeType"); $mimeType هو نوع الصياغة المُرجعة في الترويسة MIME مثل image/png وimage/gif وimage/jpeg. الكتابة إلى متغير يوجد طريقتين للكتابة إلى متغير: استخدام المخزن المؤقت للخرج (OB): ob_start(); // تمرير null للكتابة افتراضيًا في مجرى الخرج القياسي imagepng($image, null, $quality); $binary = ob_get_clean(); استخدام مغلِّف المجرى: قد يكون لديك سبب ما لعدم استخدام المخزن المؤقت للخرج كأن يكون لديك بالفعل مخزن مؤقت قيد التشغيل لذا تحتاج إلى بديل، يمكنك تسجيل مغلَّف مجرى جديد باستخدام الدالة stream_wrapper_register لذا يمكنك تمرير مجرى إلى دالة إظهار الصورة واستعادته لاحقًا. <?php class GlobalStream{ private $var; public function stream_open(string $path){ this->var =& $GLOBALS[parse_url($path)["host"]]; return true; } public function stream_write(string $data){ $this->var .= $data; return strlen($data); } } stream_wrapper_register("global", GlobalStream::class); $image = imagecreatetruecolor(100, 100); imagefill($image, 0, 0, imagecolorallocate($image, 0, 0, 0)); $stream = fopen("global://myImage", ""); imagepng($image, $stream); echo base64_encode($myImage); في هذا المثال يكتب الصنف GlobalStream أي دخل إلى المتغير المرجعي (أي الكتابة بشكل غير مباشر إلى المتغير العام للاسم المعطى)، يمكن استرجاع المتغير العام لاحقًا بشكلٍ مباشر. يجب الانتباه إلى عدة أمور: صنف مغلِّف المجرى المنفَّذ بشكلٍ كامل يجب أن يشبه هذا الصنف لكن وفقًا للاختبارات باستخدام تابع __call السحري فإنّه من الممكن استدعاء stream_open وstream_write وstream_close فقط من الدوال الداخلية. لا توجد رايات مطلوبة في استدعاء fopen لكن يجب أن تمرر سلسلة فارغة على الأقل، لأنّ الدالة fopen تتوقع مثل هذا المعامل، حتى لو لم تستخدمها في تنفيذ stream_open يبقى هذا المعامل مطلوبًا. تستدعى الدالة stream_write عدة مرات وفقًا للاختبارات، تذكر أن تستخدم إسناد الدمج .= وليس إسناد المتغير المباشر =. مثال: في وسم <img> في HTML، يمكن توفير صورة بشكلٍ مباشر بدلًا من استخدام رابط خارجي: echo '<img src="data:image/png;base64,' . base64_encode($binary) . '">'; إنشاء صورة نستخدم الدالة imagecreatetruecolor لإنشاء صورة فارغة: $img = imagecreatetruecolor($width, $height); المتغير $img هو متغير مورد الآن بعرض $width وطول $height بكسل، لاحظ أنّ العرض يُحسب من اليسار إلى اليمين والطول من الأعلى إلى الأسفل. يمكن أن يُنشأ أيضًا مورد الصورة من دوال إنشاء الصورة مثل imagecreatefrompng وimagecreatefromjpeg ودوال imagecreatefrom* أخرى. قد تُحرَّر موارد الصورة لاحقًا عندما لا توجد مراجع إليها، لكن لتحرير الذاكرة بشكلٍ مباشر (قد يكون هذا مهمًا عند معالجة عدة صور كبيرة) يمكننا استخدام imagedestroy() على الصورة عندما لا تبقى حاجة لاستخدامها وتكون هذه ممارسة جيدة. imagedestroy($image); تحويل صورة إنّ الصور التي تُنشأ من تحويل الصور لا تعدّل الصورة حتى تُخرجها، لذا يمكن أن يكون محوِّل الصورة ببساطة عبارة عن ثلاثة أسطر من الشيفرة: function convertJpegToPng(string $filename, string $outputFile) { $im = imagecreatefromjpeg($filename); imagepng($im, $outputFile); imagedestroy($im); } اقتصاص الصورة وتغيير حجمها يمكنك استخدام الدالة imagecopyresampled إذا كان لديك صورة وتريد إنشاء صورة جديدة بأبعاد جديدة، أنشئ أولًا صورة جديدة بالأبعاد المرغوبة: // صورة جديدة $dst_img = imagecreatetruecolor($width, $height); خزّن الصورة الأصلية في متغير، يمكنك القيام بذلك باستخدام إحدى دوال createimagefrom* حيث يمكن أن تكون * هي jpeg أو gif أوpng أوstring، مثال: // الصورة الأصلية $src_img=imagecreatefromstring(file_get_contents($original_image_path)); ثم استخدم الدالة imagecopyresampled لنسخ كل الصورة الأصلية (أو جزء منها) (src_img) إلى الصورة الجديدة (dst_img): imagecopyresampled($dst_img, $src_img, $dst_x ,$dst_y, $src_x, $src_y, $dst_width, $dst_height, $src_width, $src_height); لضبط أبعاد src_* وdst_*، استخدم الصورة التالية: إذا كنت تريد الآن نسخ كامل الصورة المصدر (الأصلية) إلى كامل منطقة الهدف (بدون اقتصاص): $src_x = $src_y = $dst_x = $dst_y = 0; // عرض الصورة الجديدة $dst_width = $width; // طول الصورة الجديدة $dst_height = $height; // عرض الصورة الأصلية $src_width = imagesx($src_img); // طول الصورة الأصلية $src_height = imagesy($src_img); مكتبة Imagick التثبيت باستخدام apt في الأنظمة المعتمدة على Debian: sudo apt-get install php5-imagick باستخدام Homebrew في أنظمة OSX/macOs: brew install imagemagick باستخدام الإصدارات الثنائية: التعليمات في موقع imagemagick. الاستخدام <?php $imagen = new Imagick('imagen.jpg'); // إذا وضعت قيمة المعامل 0 ستتم المحافظة على نسبة العرض $imagen->thumbnailImage(100, 0); echo $imagen; ?> تحويل صورة إلى سلسلة نصية بالأساس 64 يُظهر هذا المثال كيفية تحويل صورة إلى سلسلة نصية بالأساس 64 (أي سلسلة نصية يمكنك استخدامها مباشرةً في السمة src لوسم img)، يستخدم هذا المثال مكتبة Imagick لكن يمكن استخدام مكتبات أخرى مثل GD. <?php // (1) $img = new Imagick('image.jpg'); // (2) $img->resizeImage(320, 240); // (3) $imgBuff = $img->getimageblob(); // (4) $img->clear(); // (5) $img = base64_encode($imgBuff); echo "<img alt='Embedded Image' src='data:image/jpeg;base64,$img' />"; في الموضع (1) يُحمَّل الملف image.jpg للمعالجة، مسار الملف نسبي إلى ملف .php المتضمن هذه الشيفرة لذا في هذا المثال يجب أن يكون ملف image.jpg في نفس مجلد السكربت. في الموضع (2) يتغير حجم الصورة للحجم المُعطى كطول وعرض وإذا أردت تغيير دقة الصورة أيضًا مع تغيير الحجم يمكنك استخدام الدالة $img->resampleimage(320, 240)، لاحظ أنّه يمكنك ضبط المعامل الثاني إلى 0 للمحافظة على نسبة عرض الصورة. في الموضع (3) تُرجع الدالة تمثيل الصورة على شكل سلسلة نصية غير مشفرة. في الموضع (4) يُزال المورد image.jpg من الكائن $img ويُدمَّر الكائن مما يحرر موارد النظام المحجوزة لمعالجة الصورة. في الموضع (5) تُنشأ نسخة بتشفير الأساس 64 من السلسلة النصية السابقة غير المشفرة ثم تُعرَض كصورة في الصفحة، لاحظ أنّه قد يتغير الجزء image/jpeg في السمة src وذلك بالاعتماد على نوع الصورة التي تستخدمها (png أوjpeg مثلًا). ترجمة -وبتصرف- للفصول [Image Processing with GD - Imagick] من كتاب PHP Notes for Professionals book اقرأ أيضًا المقال التالي: مدخل إلى تعلم الآلة (Machine learning) في PHP المقال السابق: اصطلاحات ومواضيع متفرقة مهمة لكل مبرمج PHP1 نقطة
-
شكرا على هذه المقالة الجميلة والممتعة في قراءتها. أرغب في هذا السياق أن أشير إلى النسخ العربية من دوال date و mktime و strtotime والمتاحة ضمن مكتبة Ar-PHP التي تتيح للمطور التعامل التواريخ الهجرية والأسماء العربية للأيام والأشهر بأكثر من صيغة شائعة في المشرق أو المغرب العربي.1 نقطة