لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 05/20/21 في كل الموقع
-
وسوم PHP يجب أن تستخدم دائمًا الوسوم <?php ?> أو وسوم الطباعة القصيرة <?= ?>، ويجب ألا تُستخدم الاختلافات الأخرى (خاصةً الوسوم القصيرة <? ?>) لأنّ مديري النظام يعطلونها عادةً. يجب تجاهل صيغة الإغلاق ?> عندما لا نتوقع أن ينتج الملف خرجًا لتجنب الخرج غير المقصود الذي يمكن أن يسبب مشاكل عندما يحلل العميل الملف خاصةً أنّ بعض المتصفحات تفشل في التعرف على وسم <!DOCTYPE وتنشّط نمط التجاوزات Quirks Mode. مثال عن سكربت PHP بسيط: <?php print "Hello World"; مثال عن ملف تعريف صنف: <?php class Foo { ... } مثال عن PHP مضمن في HTML: <ul id="nav"> <?php foreach ($navItems as $navItem): ?> <li><a href="<?= htmlspecialchars($navItem->url) ?>"> <?= htmlspecialchars($navItem->label) ?> </a></li> <?php endforeach; ?> </ul> فوائد المولّدات (Generators) تقدّم PHP 5.5 المولّدات والكلمة المفتاحية yield التي تسمح لنا بكتابة شيفرة غير متزامنة تبدو أشبه بالشيفرة المتزامنة، يعدّ التعبير yield مسؤولًا عن إعادة التحكم إلى الشيفرة المستدعاة وتوفير نقطة استئناف من هناك، يمكنك إرسال قيمة مع تعليمة yield، القيمة المرجعة من هذا التعبير إما null أو القيمة الممررة إلى Generator::send(). function reverse_range($i) { // مجرد وجود الكلمة المفتاحية `yield` في هذه الدالة يجعلها مولّد do { // $i هي القيمة المُحتفظ بها بين الاستئنافات print yield $i; } while (--$i > 0); } $gen = reverse_range(5); print $gen->current(); // الإرسال أيضًا يستأنف المولِّد $gen->send("injected!"); foreach ($gen as $val) { // المرور على كامل محتويات المولّد مما يجعله يستأنف عند كل تكرار echo $val; } // 5injected!4321 يمكن استخدام هذه الآلية بتنفيذ نمط مشترك (coroutine) لانتظار كائنات Awaitable المُعادة من المولِّد (بتسجيل المولّد نفسه كرد نداء للحل) ومواصلة تنفيذ المولّد بمجرد إنهاء كائن Awaitable. استخدام حلقة حدث من مكتبة Icicle تستخدم مكتبة Icicle كائنات Awaitable ومولّدات لإنشاء نمط مشترك. require __DIR__ . '/vendor/autoload.php'; use Icicle\Awaitable; use Icicle\Coroutine\Coroutine; use Icicle\Loop; $generator = function (float $time) { try { // ضبط المتغير $start إلى القيمة المعادة من الدالة microtime()? بعد $time ثانية تقريبًا $start = yield Awaitable\resolve(microtime(true))->delay($time); echo "Sleep time: ", microtime(true) - $start, "\n"; // رمي استثناء من كائن Awaitable المرفوض إلى النمط المشترك return yield Awaitable\reject(new Exception('Rejected awaitable')); } catch (Throwable $e) { // التقاط سبب رفض awaitable echo "Caught exception: ", $e->getMessage(), "\n"; } return yield Awaitable\resolve('Coroutine completed'); }; // يبقى النمط المشترك ساكنًا لمدة 1.2 ثانية ثم ينتهي معيدًا سلسلة نصية $coroutine = new Coroutine($generator(1.2)); $coroutine->done(function (string $data) { echo $data, "\n"; }); Loop\run(); إنتاج عمليات غير معطَّلة مع proc_open() لا تدعم PHP تنفيذ الشيفرة بشكلٍ متزامن إلا إذا ثبَّت الإضافات مثل pthread، يمكن تجاوز هذا أحيانًا باستخدام الدوال proc_open() وstream_set_blocking() وقراءة خرجهم بشكلٍ غير متزامن. يمكننا تنفيذ الشيفرة كعمليات فرعية متعددة إذا قسمناها إلى أجزاء أصغر، ثمّ يمكننا جعل كل عملية فرعية غير معطَّلة باستخدام دالة stream_set_blocking() أي أنّه يمكننا إنتاج عدة عمليات فرعية ثم التحقق من خرجها في حلقة (بشكل مشابه لحلقة حدث) والانتظار حتى تنتهي جميعها. يمكن أن يكون لدينا مثلًا عملية فرعية صغيرة تنفّذ حلقة وتتوقف في كل تكرار بشكلٍ عشوائي لمدة 100- 1000 ميلي ثانية (لاحظ أنّ التأخير هو نفسه لكل عملية فرعية). <?php // subprocess.php $name = $argv[1]; $delay = rand(1, 10) * 100; printf("$name delay: ${delay}ms\n"); for ($i = 0; $i < 5; $i++) { usleep($delay * 1000); printf("$name: $i\n"); } ثم ستنتج العملية الرئيسية عمليات فرعية وتقرأ خرجها، ويمكننا تقسيمه إلى كتل أصغر: إنتاج عمليات فرعية باستخدام proc_open(). جعل كل عملية فرعية غير معطَّلة باستخدام stream_set_blocking(). تنفيذ حلقة حتى تنتهي كل العمليات الفرعية باستخدام proc_get_status(). إغلاق مقابض الملف بشكل صحيح مع أنبوب الخرج لكل عملية فرعية باستخدام fclose() وإغلاق مقابض العملية باستخدام proc_close(). <?php // non-blocking-proc_open.php // واصفات الملف لكل عملية فرعية $descriptors = [ 0 => ['pipe', 'r'], // stdin 1 => ['pipe', 'w'], // stdout ]; $pipes = []; $processes = []; foreach (range(1, 3) as $i) { // إنتاج عملية فرعية $proc = proc_open('php subprocess.php proc' . $i, $descriptors, $procPipes); $processes[$i] = $proc; // جعل العملية الفرعية غير معطَّلة (أنبوب الخرج فقط) stream_set_blocking($procPipes[1], 0); $pipes[$i] = $procPipes; } // تنفيذ حلقة حتى تنتهي كل العمليات الفرعية while (array_filter($processes, function($proc) { return proc_get_status($proc)['running']; })) { foreach (range(1, 3) as $i) { usleep(10 * 1000); // 100ms // قراءة كل الخرج الممكن (الخرج غير المقروء يُخزَّن مؤقتًا) $str = fread($pipes[$i][1], 1024); if ($str) { printf($str); } } } // إغلاق كل الأنابيب والعمليات foreach (range(1, 3) as $i) { fclose($pipes[$i][1]); proc_close($processes[$i]); } يحتوي الخرج على مزيج من العمليات الفرعية الثلاث بما أننا نقرأها باستخدام fread()، لاحظ أنّه في المثال انتهت العملية proc1 قبل العمليتين الباقيتين بكثير. $ php non-blocking-proc_open.php proc1 delay: 200ms proc2 delay: 1000ms proc3 delay: 800ms proc1: 0 proc1: 1 proc1: 2 proc1: 3 proc3: 0 proc1: 4 proc2: 0 proc3: 1 proc2: 1 proc3: 2 proc2: 2 proc3: 3 proc2: 3 proc3: 4 proc2: 4 قراءة منفذ تسلسلي مع إضافة حدث ودخل/خرج مباشر إنّ مجاري الدخل والخرج المباشرة (DIO) غير معروفة الآن من قِبل الإضافة حدث Event، فلا توجد طريقة نظيفة للحصول على واصف الملف مغلفًا ضمن موارد الدخل والخرج المباشرة، إلا أنّ هناك حل بديل: فتح مجرى للمنفذ باستخدام fopen(). جعل المجرى غير معطّل باستخدام stream_set_blocking();. الحصول على واصف ملف رقمي من المجرى باستخدام EventUtil::getSocketFd();. تمرير واصف الملف الرقمي إلى الدالة dio_fdopen() والحصول على مورد دخل/خرج مباشر. إضافة حدث مع رد نداء للتنصت على الأحداث المقروءة على واصف الملف. تُصرَف البيانات المتاحة في رد النداء وتُعالج وفقًا لمنطق تطبيقك. الملف dio.php: <?php class Scanner { // مسار المنفذ مثل /dev/pts/5 protected $port; // واصف الملف الرقمي protected $fd; // EventBase protected $base; // مورد دخل/خرج مباشر protected $dio; // حدث protected $e_open; // حدث protected $e_read; public function __construct ($port) { $this->port = $port; $this->base = new EventBase(); } public function __destruct() { $this->base->exit(); if ($this->e_open) $this->e_open->free(); if ($this->e_read) $this->e_read->free(); if ($this->dio) dio_close($this->dio); } public function run() { $stream = fopen($this->port, 'rb'); stream_set_blocking($stream, false); $this->fd = EventUtil::getSocketFd($stream); if ($this->fd < 0) { fprintf(STDERR, "Failed attach to port, events: %d\n", $events); return; } $this->e_open = new Event($this->base, $this->fd, Event::WRITE, [$this, '_onOpen']); $this->e_open->add(); $this->base->dispatch(); fclose($stream); } public function _onOpen($fd, $events) { $this->e_open->del(); $this->dio = dio_fdopen($this->fd); // استدعاء دوال دخل/خرج مباشر هنا dio_tcsetattr($this->dio, [ 'baud' => 9600, 'bits' => 8, 'stop' => 1, 'parity' => 0 ]); $this->e_read = new Event($this->base, $this->fd, Event::READ | Event::PERSIST, [$this, '_onRead']); $this->e_read->add(); } public function _onRead($fd, $events) { while ($data = dio_read($this->dio, 1)) { var_dump($data); } } } // تغيير وسيط المنفذ $scanner = new Scanner('/dev/pts/5'); $scanner->run(); الاختبار: نفّذ التعليمة التالية في الطرفية A: $ socat -d -d pty,raw,echo=0 pty,raw,echo=0 2016/12/01 18:04:06 socat[16750] N PTY is /dev/pts/5 2016/12/01 18:04:06 socat[16750] N PTY is /dev/pts/8 2016/12/01 18:04:06 socat[16750] N starting data transfer loop with FDs [5,5] and [7,7] قد يختلف الخرج، استخدم الطرفيات الزائفة من أول سطرين (/dev/pts/5 و/dev/pts/8 بالتحديد). نفّذ في الطرفية B السكربت السابق، قد تحتاج إلى صلاحيات الجذر: $ sudo php dio.php أرسل من الطرفية C سلسلة نصية إلى الطرفية الزائفة الأولى: $ echo test > /dev/pts/8 الخرج: string(1) "t" string(1) "e" string(1) "s" string(1) "t" string(1) " " عميل HTTP بالاعتماد على الإضافة Event إليك مثال عن صنف عميل HTTP بالاعتماد على الإضافة Event، يسمح هذا الصنف بجدولة عدد من طلبات HTTP ثم تنفيذها بشكلٍ غير متزامن. ملف http-client.php: <?php class MyHttpClient { // متغير من الصنف EventBase protected $base; // مصفوفة كائنات من الصنف EventHttpConnection protected $connections = []; public function __construct() { $this->base = new EventBase(); } // دالة لإرسال كل الطلبات المعلقة (أحداث)، تُرجع void public function run() { $this->base->dispatch(); } public function __destruct() { // تدمير كائنات الاتصال بشكلٍ صريح، لا تنتظر كانس المهملات (GC) وإلا قد يتحرر كائن EventBase باكرًا $this->connections = null; } // (1) public function addRequest($address, $port, array $headers, $cmd = EventHttpRequest::CMD_GET, $resource = '/'){ $conn = new EventHttpConnection($this->base, null, $address, $port); $conn->setTimeout(5); $req = new EventHttpRequest([$this, '_requestHandler'], $this->base); foreach ($headers as $k => $v) { $req->addHeader($k, $v, EventHttpRequest::OUTPUT_HEADER); } $req->addHeader('Host', $address, EventHttpRequest::OUTPUT_HEADER); $req->addHeader('Connection', 'close', EventHttpRequest::OUTPUT_HEADER); if ($conn->makeRequest($req, $cmd, $resource)) { $this->connections []= $conn; return $req; } return false; } // (2) public function _requestHandler($req, $unused) { if (is_null($req)) { echo "Timed out\n"; } else { $response_code = $req->getResponseCode(); if ($response_code == 0) { echo "Connection refused\n"; } elseif ($response_code != 200) { echo "Unexpected response: $response_code\n"; } else { echo "Success: $response_code\n"; $buf = $req->getInputBuffer(); echo "Body:\n"; while ($s = $buf->readLine(EventBuffer::EOL_ANY)) { echo $s, PHP_EOL; } } } } } $address = "my-host.local"; $port = 80; $headers = [ 'User-Agent' => 'My-User-Agent/1.0', ]; $client = new MyHttpClient(); // إضافة طلبات معلقة for ($i = 0; $i < 10; $i++) { $client->addRequest($address, $port, $headers, EventHttpRequest::CMD_GET, '/test.php?a=' . $i); } // إرسال طلبات معلقة $client->run(); في الموضع (1) نضيف طلب HTTP معلق، معاملاته هي: $address: اسم المضيف أو IP، سلسلة نصية. $port: رقم المنفذ، عدد صحيح. $headers: ترويسات HTTP إضافية، مصفوفة. $cmd: ثابت EventHttpRequest::CMD_*، عدد صحيح. $resource: مورد طلب HTTP مثل '/page?a=b&c=d'، سلسلة نصية. القيمة المعادة إما EventHttpRequest أو false. في الموضع (2) نضيف دالة لمعالجة طلب HTTP، معاملاتها: $req، كائن من الصنف EventHttpRequest. $unused، خليط من المعاملات. تعيد هذه الدالة void. الملف test.php، مثال عن سكربت من جهة الخادم: <?php echo 'GET: ', var_export($_GET, true), PHP_EOL; echo 'User-Agent: ', $_SERVER['HTTP_USER_AGENT'] ?? '(none)', PHP_EOL; الاستخدام: php http-client.php مثال عن الخرج: Success: 200 Body: GET: array ( 'a' => '1', ) User-Agent: My-User-Agent/1.0 Success: 200 Body: GET: array ( 'a' => '0', ) User-Agent: My-User-Agent/1.0 Success: 200 Body: GET: array ( 'a' => '3', ) ... // الخرج مختصر لاحظ أنّ الشيفرة صُممت للمعالجة طويلة الأمد في CLI SAPI. عميل HTTP بالاعتماد على الإضافة Ev إليك مثال عن صنف عميل HTTP بالاعتماد على الإضافة Ev. تنفذ الإضافة Ev حدث حلقة بسيط لكن قوي للأغراض العامة، إنّها لا توفر مراقبين خاصين للشبكة لكن يمكن استخدام I/O watcher الخاص بالإضافة للمعالجة غير المتزامنة للمقابس. تظهر الشيفرة التالية كيف يمكن جدولة طلبات HTTP للمعالجة التفرعية. ملف http-client.php: <?php class MyHttpRequest { // كائن من الصنف MyHttpClient private $http_client; // سلسلة نصية private $address; // مورد HTTP من النوع سلسلة نصية مثل /page?get=param private $resource; // طريقة HTTP من النوع سلسلة نصية مثل GET، POST private $method; // عدد صحيح private $service_port; // مقبس مورد private $socket; // مهلة الاتصال بالثانية من النوع عدد عشري private $timeout = 10.; // حجم كل جزء للدالة socket_recv() بالبايتات من النوع عدد صحيح private $chunk_size = 20; ?// كائن من الصنف EvTimer private $timeout_watcher; // كائن من الصنف EvIo private $write_watcher; // كائن من الصنف EvIo private $read_watcher; // كائن من الصنف EvTimer private $conn_watcher; // مخزن مؤقت للبيانات القادمة من النوع سلسلة نصية private $buffer; // الأخطاء التي أخبرت عنها إضافة المقابس في وضع عدم التعطيل من النوع مصفوفة private static $e_nonblocking = [ // عُطِّلت العملية لكن وُضع واصف الملف في وضع عدم التعطيل (EAGAIN أو EWOULDBLOCK) 11, // العملية الحالية قيد التقدم (EINPROGRESS) 115, ]; // (1) public function __construct(MyHttpClient $client, $host, $resource, $method) { $this->http_client = $client; $this->host = $host; $this->resource = $resource; $this->method = $method; // الحصول على المنفذ من خدمة WWW $this->service_port = getservbyname('www', 'tcp'); // الحصول على عنوان IP للمضيف الهدف $this->address = gethostbyname($this->host); // إنشاء مقبس TCP/IP $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!$this->socket) { throw new RuntimeException("socket_create() failed: reason: " . socket_strerror(socket_last_error())); } // ضبط الراية O_NONBLOCK socket_set_nonblock($this->socket); $this->conn_watcher = $this->http_client->getLoop() ->timer(0, 0., [$this, 'connect']); } public function __destruct() { $this->close(); } private function freeWatcher(&$w) { if ($w) { $w->stop(); $w = null; } } // تحرير كل موارد الطلب private function close() { if ($this->socket) { socket_close($this->socket); $this->socket = null; } $this->freeWatcher($this->timeout_watcher); $this->freeWatcher($this->read_watcher); $this->freeWatcher($this->write_watcher); $this->freeWatcher($this->conn_watcher); } // دالة تهيئ اتصالًا بالمقبس وتعيد قيمة منطقية public function connect() { $loop = $this->http_client->getLoop(); $this->timeout_watcher = $loop->timer($this->timeout, 0., [$this, '_onTimeout']); $this->write_watcher = $loop->io($this->socket, Ev::WRITE, [$this, '_onWritable']); return socket_connect($this->socket, $this->address, $this->service_port); } // رد نداء لمهلة المراقب (EvTimer) public function _onTimeout(EvTimer $w) { $w->stop(); $this->close(); } // رد نداء يُستدعى عندما يصبح المقبس قابلًا للكتابة public function _onWritable(EvIo $w) { $this->timeout_watcher->stop(); $w->stop(); $in = implode("\r\n", [ "{$this->method} {$this->resource} HTTP/1.1", "Host: {$this->host}", 'Connection: Close', ]) . "\r\n\r\n"; if (!socket_write($this->socket, $in, strlen($in))) { trigger_error("Failed writing $in to socket", E_USER_ERROR); return; } $loop = $this->http_client->getLoop(); $this->read_watcher = $loop->io($this->socket, Ev::READ, [$this, '_onReadable']); // الاستمرار بتنفيذ الحلقة $loop->run(); } // رد نداء يُستدعى عندما يصبح المقبس قابلًا للقراءة public function _onReadable(EvIo $w) { // استقبال 20 بايت في وضع عدم التعطيل $ret = socket_recv($this->socket, $out, 20, MSG_DONTWAIT); if ($ret) { // إذا كان لا يزال هناك بيانات للقراءة، أضفها إلى المخزن المؤقت $this->buffer .= $out; } elseif ($ret === 0) { // إذا قُرِأت كل البيانات printf("\n<<<<\n%s\n>>>>", rtrim($this->buffer)); fflush(STDOUT); $w->stop(); $this->close(); return; } // التقاط EINPROGRESS، EAGAIN أو EWOULDBLOCK if (in_array(socket_last_error(), static::$e_nonblocking)) { return; } $w->stop(); $this->close(); } } ///////////////////////////////////// class MyHttpClient { // مصفوفة كائنات من الصنف MyHttpRequest private $requests = []; ?// متغير من الصنف EvLoop private $loop; public function __construct() { // ينفذ كل عميل HTTP حلقة حدث خاصة به $this->loop = new EvLoop(); } public function __destruct() { $this->loop->stop(); } // تعيد هذه الدالة كائن EvLoop public function getLoop() { return $this->loop; } // إضافة طلبات معلقة public function addRequest(MyHttpRequest $r) { $this->requests []= $r; } // إرسال كل الطلبات المعلقة public function run() { $this->loop->run(); } } //// الاستخدام $client = new MyHttpClient(); foreach (range(1, 10) as $i) { $client->addRequest(new MyHttpRequest($client, 'my-host.local', '/test.php?a=' . $i, 'GET')); } $client->run(); في الموضع (1) معاملات الدالة هي: $client من الصنف MyHttpClient. $host اسم المضيف مثل google.co.uk، سلسلة نصية $resource مورد HTTP مثل /page?a=b&c=d، سلسلة نصية. $method طريقة HTTP مثل: GET، HEAD، POST، PUT…، سلسلة نصية. ترمي هذه الدالة الاستثناء RuntimeException. الاختبار: بفرض أنّ سكربت http://my-host.local/test.php يطبع محتويات $_GET: <?php echo 'GET: ', var_export($_GET, true), PHP_EOL; سيكون عندها خرج الأمر php http-client.php مشابهًا للتالي: <<<< HTTP/1.1 200 OK Server: nginx/1.10.1 Date: Fri, 02 Dec 2016 12:39:54 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: close X-Powered-By: PHP/7.0.13-pl0-gentoo 1d GET: array ( 'a' => '3', ) 0 >>>> <<<< HTTP/1.1 200 OK Server: nginx/1.10.1 Date: Fri, 02 Dec 2016 12:39:54 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: close X-Powered-By: PHP/7.0.13-pl0-gentoo 1d GET: array ( 'a' => '2', ) 0 >>>> ... // الخرج مختصر لاحظ أنّ إضافة المقابس في PHP 5 قد تسجل تحذيرات لقيم الخطأ EINPROGRESS وEAGAIN وEWOULDBLOCK، من الممكن تعطيل هذه التسجيلات بكتابة الشيفرة: error_reporting(E_ERROR); استخدام حلقة الحدث Amp تستفاد مكتبة العمل Amp من الوعود (اسم آخر لكائنات Awaitables) والمولِّدات لإنشاء نمط مشترك. require __DIR__ . '/vendor/autoload.php'; use Amp\Dns; // جرب الأسرع محللنا المعرّف من قِبل النظام أو غوغل function queryStackOverflow($recordtype) { $requests = [ Dns\query("stackoverflow.com", $recordtype), Dns\query("stackoverflow.com", $recordtype, ["server" => "8.8.8.8"]), ]; // تعيد وعدًا ينتهي عندما ينتهي أول طلب return yield Amp\first($request); } \Amp\run(function() { // الحلقة الأساسية، نمط مشترك ضمنيًا try { // التحويل إلى نمط مشترك باستخدام Amp\resolve() $promise = Amp\resolve(queryStackOverflow(Dns\Record::NS)); list($ns, $type, $ttl) = // نحتاج إلى نتيجة NS واحدة وليس كل النتائج current(yield Amp\timeout($promise, 2000 /* milliseconds */)); echo "The result of the fastest server to reply to our query was $ns"; } catch (Amp\TimeoutException $e) { echo "We've heard no answer for 2 seconds! Bye!"; } catch (Dns\NoRecordException $e) { echo "No NS records there? Stupid DNS nameserver!"; } }); التوطين (Localization) توطين السلاسل النصية مع gettext() gettext من مكتبة GNU هي إضافة PHP يجب تضمصينها ضمن ملف php.ini: extension=php_gettext.dll #Windows extension=gettext.so #Linux تنفذ دوال gettext واجهة برمجة تطبيقات دعم اللغة الأصلية (NLS) والتي يمكن استخدامها لتوطين تطبيقات PHP. يمكن إجراء السلاسل النصية للترجمة في PHP بضبط المحلية (locale) وضبط جداول الترجمة واستدعاء gettext() على أي سلسلة نصية تريد ترجمتها. <?php // ضبط اللغة إلى الفرنسية putenv('LC_ALL= fr_FR'); setlocale(LC_ALL, 'fr_FR'); // تحديد موقع جداول الترجمة للنطاق 'myPHPApp' bindtextdomain("myPHPApp", "./locale"); // اختيار النطاق 'myPHPApp' textdomain("myPHPApp"); الملف myPHPApp.po: #: /Hello_world.php:56 msgid "Hello" msgstr "Bonjour" #: /Hello_world.php:242 msgid "How are you?" msgstr "Comment allez-vous?" تحمّل الدالة gettext() ملف .po بعد تصريفه أي ملف .mo، الذي يربط ملفك ليصبح سلاسل نصية مترجمة كما في الأعلى. بعد هذه الشيفرة البسيطة سيُنظر إلى الترجمة في الملف التالي: ./locale/fr_FR/LC_MESSAGES/myPHPApp.mo عندما تستدعي gettext('some string')، إذا كانت السلسلة النصية 'some string' مُترجمة في الملف .mo ستُرجع الترجمة وإلا ستُرجع السلسلة 'some string' غير مترجمة. // طباعة النسخة المترجمة من 'Welcome to My PHP Application' echo gettext("Welcome to My PHP Application"); // أو نستخدم الاسم البديل `_()` للدالة `gettext()` echo _("Have a nice day"); معالجة الترويسات الضبط الأساسي للترويسة إليك الضبط الأساسي لترويسة للانتقال إلى صفحة جديدة عند الضغط على زر: if(isset($_REQUEST['action'])) { switch($_REQUEST['action']) { // ضبط الترويسة بالاعتماد على أي الزر المضغوط case 'getState': header("Location: http://NewPageForState.com/getState.php?search=" . $_POST['search']); break; case 'getProject': header("Location: http://NewPageForProject.com/getProject.php?search=" . $_POST['search']); break; } else { GetSearchTerm(!NULL); } // نماذج لإضافة ولاية أو مشروع والضغط على البحث function GetSearchTerm($success) { if (is_null($success)) { echo "<h4>You must enter a state or project number</h4>"; } echo "<center><strong>Enter the State to search for</strong></center><p></p>"; // استخدام `$_SERVER['PHP_SELF']` يبقينا في الصفحة حتى تقرر تعليمة `switch` أين سنذهب echo "<form action='" . $_SERVER['PHP_SELF'] . "' enctype='multipart/form-data' method='POST'> <input type='hidden' name='action' value='getState'> <center>State: <input type='text' name='search' size='10'></center><p></p> <center><input type='submit' name='submit' value='Search State'></center> </form>"; GetSearchTermProject($success); } function GetSearchTermProject($success) { echo "<center><br><strong>Enter the Project to search for</strong></center><p></p>"; echo "<form action='" . $_SERVER['PHP_SELF'] . "' enctype='multipart/form-data' method='POST'> <input type='hidden' name='action' value='getProject'> <center>Project Number: <input type='text' name='search' size='10'></center><p></p> <center><input type='submit' name='submit' value='Search Project'></center> </form>"; } ?> كيفية كشف عنوان IP لعميل الاستخدام المناسب للترويسة HTTPXFORWARDED_FOR يوجد متغير آخر يُستخدم بشكلٍ سيء على نطاق واسع في ضوء أحدث ثغرات httpoxy، تُستخدم الترويسة HTTP_X_FORWARDED_FOR غالبًا لكشف عنوان IP لعميل، لكن قد يؤدي ذلك بدون أي عمليات تحقق إضافية إلى مشاكل في الأمان خاصةً عند استخدام عنوان IP هذا لاحقًا للمصادقة أو في استعلامات SQL بدون تعقيم. تتجاهل معظم أمثلة الشيفرة المتوفرة حقيقة أنّه يمكن أن نعد HTTP_X_FORWARDED_FOR معلومةً يوفرها العميل بنفسه ولذا فهي مصدر غير موثوق لاكتشاف عنوان IP العميل، تضيف بعض هذه الأمثلة تحذيرًا بشأن سوء الاستخدام المحتمل لكنها لا تزال تفتقد إلى القيام بالتحقق في شيفرتها، لذا نقدم لك مثالًا عن دالة مكتوبة في PHP عن كيفية كشف عنوان IP لعميل إذا كنت تعرف أنّ العميل يستخدم وكيلًا (proxy) وأنت تعرف أنّه يمكن الوثوق بهذا الوكيل، إذا لم تكن تعرف أي وكيل موثوق فيمكنك استخدام REMOTE_ADDR فقط. function get_client_ip() { // لا يوجد شيء لفعله بدون معلومات موثوقة if (!isset($_SERVER['REMOTE_ADDR'])) { return NULL; } // الترويسة التي يستخدمها الوكيل الموثوق للإشارة إلى عنوان IP الأصلي $proxy_header = "HTTP_X_FORWARDED_FOR"; // (1) $trusted_proxies = array("2001:db8::1", "192.168.50.1"); if (in_array($_SERVER['REMOTE_ADDR'], $trusted_proxies)) { // الحصول على عنوان IP للعميل الذي يستخدم وكيل موثوق if (array_key_exists($proxy_header, $_SERVER)) { // (2) $client_ip = trim(end(explode(",", $_SERVER[$proxy_header]))); // التحقق فقط في حالة if (filter_var($client_ip, FILTER_VALIDATE_IP)) { return $client_ip; } else { // (3) } } } // في كل الحالات الباقية REMOTE_ADDR هو عنوان IP الوحيد الذي يمكن الوثوق به return $_SERVER['REMOTE_ADDR']; } print get_client_ip(); في الموضع (1) نضيف قائمة بكل الوكلاء المعروفين لمعالجة 'proxy_header' بطريقةٍ آمنةٍ. في الموضع (2) يمكن أن تحتوي الترويسة على عدة عناوين IP لوكلاء تمر عبرها، يمكن الوثوق فقط بعنوان IP الذي أضافه الوكيل الأخير (الموجود في القائمة). في الموضع (3) فشل التحقق مما يعني فوز الشخص الذي ضَبط الوكيل أو أنشأ قائمة الوكلاء الموثوقين لذا يجب إضافة معالجة للأخطاء هنا والتنبيه على خطأ الشخص المسؤول. ترجمة -وبتصرف- للفصول [Coding Conventions - Asynchronous programming - Localization - Headers Manipulation - How to Detect Client IP Address] من كتاب PHP Notes for Professionals book اقرأ أيضًا المقال التالي: معالجة الصور مع مكتبة GD ومكتبة Imagick في PHP المقال السابق: التعامل مع واجهة سطر الأوامر (CLI) في PHP1 نقطة
-
دائما ما أسمع عن مصطلح Thread Pool في Node.js فماهو وكيف نستخدمه وماهي فائدته ؟1 نقطة
-
سلام عليكم المودال بيظهر بالشكل التالي عندي الكود كالتالي <?php include("includes/db.php"); $title = "الموردين"; ?> <!doctype html> <html lang="ar"> <head> <style> .table-responsive{ overflow-x: visible !important; } tbody{ margin-bottom: 2rem; } .more{ margin-top: 2rem !important; } .dropdown-menu-main{ top: 40px !important; } </style> <?php include("includes/head.php"); ?> </head> <body> <?php include("includes/loader.php"); ?> <div class="app-container app-theme-white body-tabs-shadow fixed-sidebar fixed-header"> <?php include("includes/app-header.php"); ?> <?php include("includes/ui-theme-settings.php"); ?> <div class="app-main"> <?php include("includes/sidebar.php"); ?> <div class="app-main__outer"> <div class="app-main__inner"> <div class="app-page-title"> <div class="page-title-wrapper"> <div class="page-title-heading"> <div class="page-title-icon"> <i class="pe-7s-box2 icon-gradient bg-mean-fruit"> </i> </div> <div>الموردين <div class="page-title-subheading">إستعراض الموردين </div> </div> </div> <div class="page-title-actions"> <button type="button" data-toggle="tooltip" data-placement="bottom" class="btn-shadow mr-3 btn btn-dark"> <i class="fa fa-star"></i> </button> </div> </div> </div> <table class="table table-primary table-bordered" dir="rtl"> <thead> <tr class="table-primary" align="center"> <th scope="col">الكود</th> <th scope="col">اسم الشركة</th> <th scope="col">بنود التوريد</th> <th scope="col">إعدادات</th> </tr> </thead> <tbody> <?php //الاستعلام $result = $conn->query("SELECT * FROM supplier ORDER BY id DESC"); // تعريف المتغيرات while ($row = $result->fetch_assoc()) { //تعريف بنود التوريد $supplying_data = ' <select class="form-select form-select-lg mb-3" aria-label="بنود التوريد"> <option selected>افتح لمشاهدة بنود التوريد</option> '; if ($row['sFabric'] == "true") $supplying_data .= '<option>قماش</option> '; if ($row['sAcc'] == "true") $supplying_data .= '<option>اكسسوارات</option>'; if ($row['sCarton'] == "true") $supplying_data .= '<option>كرتون</option>'; if ($row['sLine'] == "true") $supplying_data .= '<option>خيوط</option>'; if ($row['sNeedle'] == "true") $supplying_data .= '<option>إبر حياكة</option>'; if ($row['sMac'] == "true") $supplying_data .= '<option>مكن</option>'; if ($row['sBag'] == "true") $supplying_data .= '<option>أكياس</option>'; $supplying_data .= '</select> '; //نهاية تعريف بنود التوريد //الجدول echo' <tr class="table-light" align="center"> <th scope="row">' . $row['id'] . '</th> <td >' . $row['facory'] . '</td> <td>' . $supplying_data . '</td> <td align="center"> <button type="button" class="btn btn-info" data-toggle="modal" data-target="#more">عرض المزيد</button> <button type="button" class="btn btn-primary">تعديل</button> <button type="button" class="btn btn-danger">حذف</button> </td> </tr>'; } ?> </tbody> </table> } <?php include("includes/footer.php"); ?> <!-- Starting More Modal --> <!-- Modal --> <!-- Modal --> <div class="modal fade" id="more" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">بيانات المورد</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="table-responsive text-center"> <table class="align-middle mb-0 table table-borderless table-striped table-hover"> <thead> <tr> <th class="text-center">إسم الشركة/المورد</th> <th class="text-center">إسم صاحب الشركة</th> <th class="text-center">العنوان</th> <th class="text-center">بيانات الموردين</th> </tr> </thead> <tbody> <tr> <th class="text-center facory"></th> <th class="text-center oName"></th> <th class="text-center address"></th> <th class="text-center "> <div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> بيانات التوريد </button> <div class="dropdown-menu supplying_data" aria-labelledby="dropdownMenu2"> </div> </div> </th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">رقم التليفون</th> <th colspan="2" class="text-center">رقم الواتساب</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center phone"></th> <th colspan="2" class="text-center whatsapp"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">البريد الإلكترونى</th> <th colspan="2" class="text-center">الويب سايت</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center email"></th> <th colspan="2" class="text-center website"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">صفحة الفيسبوك</th> <th colspan="2" class="text-center">تيليجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center facebook"></th> <th colspan="2" class="text-center telegram"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">تويتر</th> <th colspan="2" class="text-center">انستجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center twitter"></th> <th colspan="2" class="text-center instagram"></th> </tr> </tbody> <thead> <tr> <th colspan="4" class="text-center" style="color:blue;">بيانات التواصل غير المالك</th> </tr> </thead> <thead> <tr> <th colspan="2" class="text-center">رقم التليفون</th> <th colspan="2" class="text-center">رقم الواتساب</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center phone2"></th> <th colspan="2" class="text-center whatsapp2"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">البريد الإلكترونى</th> <th colspan="2" class="text-center">الويب سايت</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center email2"></th> <th colspan="2" class="text-center website2"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">صفحة الفيسبوك</th> <th colspan="2" class="text-center">تيليجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center facebook2"></th> <th colspan="2" class="text-center telegram2"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">تويتر</th> <th colspan="2" class="text-center">انستجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center twitter2"></th> <th colspan="2" class="text-center instagram2"></th> </tr> </tbody> <thead> <tr> <th colspan="4" class="text-center" style="color:blue;">كود QR</th> </tr> </thead> <thead> <tr> <th colspan="4" class="text-center"> <img id="qr" src="#"> </th> </tr> </thead> </table> </div> </div> <div class="modal-footer text-center"> <button type="button" class="btn btn-secondary" data-dismiss="modal">إغلاق</button> </div> </div> </div> </div> <!-- Endind More Modal --> <?php include("includes/js.php"); ?> </body> </html> ممكن اعرف ايه الخطأ اللي عندي اللي بيخلي الصفحة تتجمد اول ما افتح المودال و ما بعرفش اعمل اي حاجة تاني1 نقطة
-
السيرفر الخاص بي يعمل على Node.js ويتم إستقبال صوره بتشفير base64 data:image/jpeg;base64,/9j/4QCcRXhpZgAASUkqAAgAAAA ... CiiigD//Z هذه البيانات التي يتم إستقبالها من المفترض أن يتم حفظها على شكل صورة بصيغة jpg وبالتالي أستخدم Buffer و FileSystemWriter لتحقيق ذلك var imageBuffer = new Buffer(data, 'base64'); fs.writeFile("test.jpg", imageBuffer, function(err) {}); ولكن عند فتح ملف الصورة الناتجة بأي برنامج يظهر خطأ File is damaged or too big على الرغم من عدم حدوث أي أخطاء أثناء العمليه فما الحل ؟1 نقطة
-
لدي سكريب Powrshell على نفس السيرفر الذي أستخدم عليه Node.js وأريد تنفيذ هذا السكريبت قمت بالبحث ووجدت أنه يمكنني تنفيذ ذلك بإستخدام "child_process" ولكنني لا أعلم ماهو "child_process" وكيف يمكنني إستخدامه لتنفيذ سكريبت powershell داخل Node.js. فهل يمكنكم المساعدة ؟1 نقطة
-
اتاسف في التاخر في الرد يا اخي, افادة الناس ليس ازعاجا مشاركة افكاري ليس مضيعة للوقت ونحن نكمل بعضنا البعض والتوفيق يا اخي الكريم !1 نقطة
-
يمكنك إعداد أو تغير متغيرات البيئة من خلال تغير قيمة start في ملف package.json كالتالي: // قم بتعديل السطر التالي "start": "webpack serve" // ليكون مثل هذا السطر "start": "set NODE_ENV=development && webpack serve" على Linux و Mac نستعمل export بدلًا من set كالتالي: "start": "export NODE_ENV=development && webpack serve"1 نقطة
-
يمكن تغير قمية المتغير NODE_ENV من ملف package.json كالتالي لو نظام التشغيل ويندوز "scripts": { "start-prod": "SET NODE_ENV=production & node index.js", "start-dev": "SET NODE_ENV=development & node index.js" }, لو نظام التشغيل لينكس "scripts": { "start-prod": "NODE_ENV=production & node index.js", "start-dev": "NODE_ENV=development & node index.js" }, ثم لتشغيل في ال production npm run start-prod او في development mode npm run start-dev1 نقطة
-
لأستخدم environment variables تحتاج لتسطيب dotenv package npm install dotenv --save ثم تقوم بالوصل إلي المتغيرات داخل ملف .env كالتالي // تقوم بإستدعاء dotenv package require('dotenv').config(); // ثم للحصول قيمة ال port const PORT = process.env.PORT // 3000 بفرض ان ملف .env يحتوي علي PORT // .env file conten PORT=30001 نقطة
-
سلام عليكم انا عامل صفحة استعرااض لبيانات من الداتابيز و للمزيد عامل زر مودال يفتح عالتفاصيل صفحة الاستعراض الاستلام شغال فيها هايل و مفيهوش اي مشاكل بفضل الله ظاهرلي البيانات الرئيسيه عادي الكود اسم المورد بيانات التوريد جدول منتظم ليس به مشكلة لكن مثلا حبيت اضغط على زر عرض التفاصيل ليفتح لي تفاصيل البيان رقم 108 مثلا في نافذة المودال كل ما بداخل نافذة المودال ما بيقراش غير اول ريكورد بالداتابيز بس يعني اول id عندي رقم 100 لو ضغطت عرض المزيد يظهرلي بياناته فقط حتى لو ضغطت على عرض المزيد لاي كود اخر ما هو خطأي و شكرا لكم <?php include("includes/db.php"); $title = "الموردين"; ?> <!doctype html> <html lang="ar"> <head> <style> .table-responsive{ overflow-x: visible !important; } tbody{ margin-bottom: 2rem; } .more{ margin-top: 2rem !important; } .dropdown-menu-main{ top: 40px !important; } </style> <?php include("includes/head.php"); ?> </head> <body> <?php include("includes/loader.php"); ?> <div class="app-container app-theme-white body-tabs-shadow fixed-sidebar fixed-header"> <?php include("includes/app-header.php"); ?> <?php include("includes/ui-theme-settings.php"); ?> <div class="app-main"> <?php include("includes/sidebar.php"); ?> <div class="app-main__outer"> <div class="app-main__inner"> <div class="app-page-title"> <div class="page-title-wrapper"> <div class="page-title-heading"> <div class="page-title-icon"> <i class="pe-7s-box2 icon-gradient bg-mean-fruit"> </i> </div> <div>الموردين <div class="page-title-subheading">إستعراض الموردين </div> </div> </div> <div class="page-title-actions"> <button type="button" data-toggle="tooltip" data-placement="bottom" class="btn-shadow mr-3 btn btn-dark"> <i class="fa fa-star"></i> </button> </div> </div> </div> <table class="table table-primary table-bordered" dir="rtl"> <thead> <tr class="table-primary" align="center"> <th scope="col">الكود</th> <th scope="col">اسم الشركة</th> <th scope="col">بنود التوريد</th> <th scope="col">إعدادات</th> </tr> </thead> <tbody> <?php //الاستعلام $result = $conn->query("SELECT * FROM supplier ORDER BY id DESC"); // تعريف المتغيرات while ($row = $result->fetch_assoc()) { //تعريف بنود التوريد $id = $row['id']; $factory = $row['facory']; $supplying_data = ' <select class="form-select form-select-lg mb-3" aria-label="بنود التوريد"> <option selected>افتح لمشاهدة بنود التوريد</option> '; if ($row['sFabric'] == "true") $supplying_data .= '<option>قماش</option> '; if ($row['sAcc'] == "true") $supplying_data .= '<option>اكسسوارات</option>'; if ($row['sCarton'] == "true") $supplying_data .= '<option>كرتون</option>'; if ($row['sLine'] == "true") $supplying_data .= '<option>خيوط</option>'; if ($row['sNeedle'] == "true") $supplying_data .= '<option>إبر حياكة</option>'; if ($row['sMac'] == "true") $supplying_data .= '<option>مكن</option>'; if ($row['sBag'] == "true") $supplying_data .= '<option>أكياس</option>'; $supplying_data .= '</select> '; //نهاية تعريف بنود التوريد //الجدول echo' <tr class="table-light" align="center"> <th scope="row">' . $row['id'] . '</th> <td >' . $row['facory'] . '</td> <td>' . $supplying_data . '</td> <td align="center"> <button class="btn btn-outline-primary m-auto mt-2 more" data-toggle="modal" data-target="#more">عرض المزيد</button> <!-- زر التعديل --> <a href="edit_supplier.php?id=' . $row['id'] . '" target="_blank"></a> <button type="button" class="btn btn-primary">تعديل</button> <!-- زر الحذف --> <button type="button" class="btn btn-danger">حذف</button> </td> </tr>'; } ?> </tbody> </table> </div> </div> </div> <?php include("includes/footer.php"); ?> </div> <!-- Starting More Modal --> <!-- Modal --> <!-- Modal --> <div class="modal fade" id="more" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">بيانات المورد</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="table-responsive text-center"> <table class="align-middle mb-0 table table-borderless table-striped table-hover"> <thead> <tr> <th class="text-center">إسم الشركة/المورد</th> <th class="text-center">إسم صاحب الشركة</th> <th class="text-center">العنوان</th> <th class="text-center">بيانات الموردين</th> </tr> </thead> <tbody> <tr> <th class="text-center"> <?php echo $factory; ?> </th> <th class="text-center oName"></th> <th class="text-center address"></th> <th class="text-center "> <div class="dropdown"> <?php echo $supplying_data; ?> </div> </div> </th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">رقم التليفون</th> <th colspan="2" class="text-center">رقم الواتساب</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center phone"></th> <th colspan="2" class="text-center whatsapp"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">البريد الإلكترونى</th> <th colspan="2" class="text-center">الويب سايت</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center email"></th> <th colspan="2" class="text-center website"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">صفحة الفيسبوك</th> <th colspan="2" class="text-center">تيليجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center facebook"></th> <th colspan="2" class="text-center telegram"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">تويتر</th> <th colspan="2" class="text-center">انستجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center twitter"></th> <th colspan="2" class="text-center instagram"></th> </tr> </tbody> <thead> <tr> <th colspan="4" class="text-center" style="color:blue;">بيانات التواصل غير المالك</th> </tr> </thead> <thead> <tr> <th colspan="2" class="text-center">رقم التليفون</th> <th colspan="2" class="text-center">رقم الواتساب</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center phone2"></th> <th colspan="2" class="text-center whatsapp2"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">البريد الإلكترونى</th> <th colspan="2" class="text-center">الويب سايت</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center email2"></th> <th colspan="2" class="text-center website2"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">صفحة الفيسبوك</th> <th colspan="2" class="text-center">تيليجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center facebook2"></th> <th colspan="2" class="text-center telegram2"></th> </tr> </tbody> <thead> <tr> <th colspan="2" class="text-center">تويتر</th> <th colspan="2" class="text-center">انستجرام</th> </tr> </thead> <tbody> <tr> <th colspan="2" class="text-center twitter2"></th> <th colspan="2" class="text-center instagram2"></th> </tr> </tbody> <thead> <tr> <th colspan="4" class="text-center" style="color:blue;">كود QR</th> </tr> </thead> <thead> <tr> <th colspan="4" class="text-center"> <img id="qr" src="#"> </th> </tr> </thead> </table> </div> </div> <div class="modal-footer text-center"> <button type="button" class="btn btn-secondary" data-dismiss="modal">إغلاق</button> </div> </div> </div> </div> <!-- Endind More Modal --> <?php include("includes/js.php"); ?> </body> </html>1 نقطة
-
1 نقطة
-
الظاهر أن خلفية المودل modal-backdrop تظهر فوق المودل modal نفسه . يحدث هذا السلوك عادة بسبب إمتلاك الحاوي لعنصر المودل الوضعية الثابتة fixed أو النسبية relative لذلك ينصح دوما بوضع المودل قبل وسم إغلاق الـ body مباشرة لتجنب هاته المشكلة أو التأكد من إمتلاك كل الحاويات لعنصر المودل الوضعية الإفتراضية . لكن في هاته الحالة ألحظ أنه يوجد بعض الوسوم المفتوحة التي لم يتم إغلاقها . في هاته الحالة تم إعتبارها كحاويات لما بعدها من عناصر مفتوحة أو مغلقة متضمنة المودل نفسه . لاحظ : <div class="app-container app-theme-white body-tabs-shadow fixed-sidebar fixed-header"> <div class="app-main"> <div class="app-main__outer"> <div class="app-main__inner"> <div class="app-page-title"> </div> # < < < من المفروض وجود وسم إغلاق # < < < من المفروض وجود وسم إغلاق # < < < من المفروض وجود وسم إغلاق # < < < من المفروض وجود وسم إغلاق <table class="table table-primary table-bordered" dir="rtl"> ... </table> لاحظ أيضا أن الحاوي الأول (عدم إغلاقه سيجعله حاويا لكل الوسوم المغلقة أو المفتوحة بعده) يملك الوضعية الثابتة بسبب الصفوف fixed-header و fixed-sidebar و هذا غالبا ما أدى إلى ظهور المشكلة . فحل المشكلة ببساطة هو في إغلاق الوسوم المفتوحة . إذ لم يكن هذا كفيلا بحل المشكلة يمكنك إعادة ترتيب العناصر على المحور Z عن طريق الخاصية z-index . فعندما تتداخل العناصر يمكن عن طريق تحديد قيمة هذه الخاصية تحديد ما هو العنصر الذي سيظهر أعلى بقية العناصر . يمكنك إعطاء العنصر بالصف modal-backdrop قيمة أقل : .modal-backdrop { z-index: -1; }1 نقطة
-
العدد الزوجي هو العدد باقي قيسمته علي 2 يساوي صفر 6 % 2 باقي القسمه يساوي 0 وهي 6 / 2 = 3 وباقي القسمه صفر 7 % 2 باقي القسمه يساوي 1 وهي 7 / 2 = 3 وباقي القسمه 1 لان 2 * 3 = 6 مش 7 #include <iostream> using namespace std; // تعريف طول وعرض المصفوفات كثوابت #define WIDTH 3 #define HEIGHT 3 int main() { // تعريف مصفوفه بقيم مختلفه int SweetArray[WIDTH][HEIGHT] = { {1,2,3}, {4,5,6}, {7,8,9} }; // طباعة الاعداد الزوجيه cout<<"Even Numbers:\n"; for(int i = 0; i < WIDTH; i++) for(int j = 0; j < HEIGHT; j++) // العدد زوجي إذا كان باقي قيسمته علي 2 يساوي صفر // 6 % 2 = 0 زوجي // 7 % 2 = 1 فردي if(SweetArray[i][j] % 2 ==0) cout<<SweetArray[i][j]<< " "; cout<<"\n"; return 0; }1 نقطة
-
1 نقطة
-
لجمع اكتر من martix وحسب المتوسط لابد أن يكون لهم جميعا نفس الطول و العرض هذا مثلا لجمع 5 مصفوفات 2 * 2 وحساب المتوسط في مصفوفه جديده 2 * 2 #include <iostream> using namespace std; // تعريف طول وعرض المصفوفات كثوابت #define WIDTH 2 #define HEIGHT 2 int main() { // تعريف خمس مصفوفات بقيم مختلفه int arr1[WIDTH][HEIGHT] = {{1,2}, {3,4}}; int arr2[WIDTH][HEIGHT] = {{6,3}, {4,5}}; int arr3[WIDTH][HEIGHT] = {{4,2}, {8,4}}; int arr4[WIDTH][HEIGHT] = {{10,12}, {13,14}}; int arr5[WIDTH][HEIGHT] = {{2,5}, {22,20}}; int result[WIDTH][HEIGHT] = {}; // حساب المتوسط مجموع كل عنصر علي ما يقابله في المصفوفات الاخري مقسوم بعدد المصفوفات // (arr1[0][0] + arr2[0][0] + arr3[0][0] + arr4[0][0] + arr5[0][0]) / 5 => 4 for(int i = 0; i < WIDTH; i++) for(int j = 0; j < HEIGHT; j++) result[i][j] = (arr1[i][j] + arr2[i][j] + arr3[i][j] + arr4[i][j] + arr5[i][j]) / 5; // طباعة مصفوفة النتيجه cout<<"Result: \n"; for(int i = 0; i < WIDTH; i++){ for(int j = 0; j < HEIGHT; j++) { cout<<result[i][j]<<" "; } cout<<"\n"; } return 0; } الناتج: Result: 4 4 10 91 نقطة
-
1 نقطة
-
قمت بحل المشكلة .. كان سبب المشكلة ف التصميم ف GridView حياكم الله جميعاً1 نقطة
-
أحتاج الى عمل كود يمنع تكرار نفس الcode أكثر من مره جربت هذا الكود يظهر لي ايرور في rowcount if (isset($_POST['code'])) $code=$_POST['code']; $query="SELECT code FROM types WHERE code =:$code "; $query_res=$conn->query($query); $count=rowcount($query_res->fetchAll()); if($count > 0){ echo "code is already taken"; } else { } php , mssql شكرا لكم1 نقطة
-
يمكنك إستخدام شهادات SSL في nodejs كالتالي: const https = require('https'); const fs = require('fs'); // تستخدم للتعامل مع الملفات، وسنحتاجها لقراءة محتوى ملفات pem // key.pem و cert.pem عبارة عن مشارات ملفات الشهادة والمفتاح const options = { key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem') }; // نقوم بتمرير الإعدادات السابقة في التابع createServer // مع إرسال رسالة Hello, World! للتأكد من أن كل شيء يعمل بشكل سليم https.createServer(options, function (req, res) { res.writeHead(200); res.end("Hello, World!\n"); }).listen(8000); الآن يمكنك التوجه إلى الرابط التالي للتأكد من أن شهادة SSL تعمل: https://localhost:80001 نقطة
-
dirname__ تقوم بإرجاع المسار الكامل للملف الموجودة فيه، بينما "/." تقوم بإرجاع مسار المجلد الذي تم تنفيذ الكود منه، ولتوضيح الفرق أكثر هنا مثال للتوضيح، لنفترض أن لدينا ملف باسم script.js موجود في مجلد dist والذي بدوره موجود داخل مجلد root، أي أن هيكلية الملفات كالتالي: |-- root |-- dist |--- script.js الآن لنكتب الكود التالي داخل ملف script.js: var path = require("path"); console.log(". = %s", path.resolve(".")); console.log("__dirname = %s", path.resolve(__dirname)); الكود السابق يقوم بطباعة مسار المجلد بالطريقتين، الآن نقوم بتشغيل الملف من داخل المجلد dist (أي نقوم بالدخول إلى المجلد أولًا قبل تشغيل الملف) ولنرى النتيجة: cd /root/dist node script.js وستكون النتيجة كالتالي: . = /root/dist __dirname = /root/dist الآن نقوم بتشغيل البرنامج من داخل مجلد root كالتالي: cd /root node dist/script.js وستكون النتيجة كالتالي: . = /root __dirname = /root/dist هنا يتضح الفرق بين الطريقتين، وهو أن dirname__ تقوم دائمًا بإرجاع مسار المجلد الموجودة فيه، بينما "/." تقوم بإرجاع مسار مجلد العمل الحالي current working directory1 نقطة
-
بالنسبة لتعلم ادوبي اليستراتور ففي هذه الحالة تعلمه يزيدك من علمك وقدراتك واكتساب مهارات جديدة لكن لايعني انك لاتستطيع التصميم في الفوتوشوب فهناك فروقات ومميزات تخص كل برنامج لكن من رائي ان تتعلم شئء ثم تتعلم شئء ثاني وان تصمم تصاميمك وترفعها على الانستا او اي منصة اخرى . بارك الله فيك اخي الكريم لم افعل سوى واجبي اعجبتني لانك غير مستعجل اطلاقا وهذا اهم شئء في النجاح ان تكون صبورا وان تصبح مميزا عقليتك عقلية الناجحين وان شاء الله تتطور مع الوقت وتحقق مرادك وتنجح ,1 نقطة
-
#include <iostream> using namespace std; // نقوم بتعريف ثوابط بطول وعرض المصفوفه #define WIDTH 5 #define HIGHT 3 int main() { // 2D Matrix نعرف المصفوفه ونعطيها ارقام عشوائيه float SweetClass[WIDTH][HIGHT] = { {1, 2, 3.0}, {6, 7, 8.20}, {11, 12, 13}, {10.2, 5, 3.2}, {12, 1.2, 5.6} }; // 1D matrix نعرف مصفوفه بقيمه فارغه float MTRX[WIDTH * HIGHT] = {}; // نقوم بدوران حول المصفوفه طول وعرض ووضع القيمه في مصفوفة النتيجه for(int i = 0; i < WIDTH; i++) for(int j = 0; j < HIGHT; j++) // نقوم بحساب مكان العنصر في مصفوفة النتيجه كالاتي i * HIGHT + j // 0 * 3 + 0 = 0; 0 * 3 + 1 = 1; 0 * 3 + 2 = 2 // 1 * 3 + 0 = 3; 1 * 3 + 1 = 4; 1 * 3 + 2 = 5 MTRX[i * HIGHT + j] = SweetClass[i][j]; // نقوم بطباعة مصفوفة النتيجه for(int i = 0; i < WIDTH * HIGHT; i++) cout<<MTRX[i]<<" "; cout<<"\n"; return 0; }1 نقطة
-
تتبادر إلى أذهان معظم النّاس عندما يسمعون عبارة "تجربة المستخدم" تلك المُخطّطات المكوّنة من مستطيلات وخطوط، والكثير منهم يظنّون -مُخطئين- أنّ هذه رسم المُخطّطات (الّتي نسمّيها wireframes) هي كلّ ما في تجربة المُستخدم. فهرس سلسلة مدخل إلى تجربة المستخدم: مدخل إلى تجربة المستخدم User Experience فهم ودراسة المستخدمين في مجال تجربة المستخدم دراسة الشريحة المستهدفة في مجال تجربة المستخدم كيفية التصميم للأجهزة المختلفة هندسة المعلومات في تجربة المستخدم تعرف على أنماط التصميم في مجال تجربة المستخدم أشياء لا يمكن اعتبارها رسوما تخطيطية (Wireframes) في مجال تجربة المستخدم تعرف على الرسوم التخطيطية (Wireframes) في مجال تجربة المستخدم (هذا الدرس) مفهوم الثقل المرئي (Visual Weight) والألوان في مجال تجربة المستخدم التكرار ومخالفة الأنماط في مجال تجربة المستخدم المحاذاة والقرب في مجال تجربة المستخدم تعرف على أساليب مسح الواجهة والتراتب المرئي في مجال تجربة المستخدم أساليب الإطلاع في مجال تجربة المستخدم: التصفح، البحث والاكتشاف تصميم هيكل صفحة الويب والعناصر الأساسية في مجال تجربة المستخدم الأزرار، النماذج والدعوات إلى الإجراء في مجال تجربة المستخدم استخدام علم النفس في مجال تجربة المستخدم لتكييف المستخدم وإقناعه كيف تغير الخبرة من تجربة المستخدم؟ تصميم تجربة المستخدم من خلال بيانات وإحصائيات المستخدمين تعرف على أنواع المخططات الإحصائية في مجال تجربة المستخدم اختبارات أ/ب (A/B Test) في مجال تجربة المستخدم ما هي الرسوم التخطيطية؟ إن كنت قد تابعت الدّروس الماضية من هذه السلسلة، فلعلّك تفهم الآن أنّ تجربة المُستخدم كجبل الجليد من حيث أنّ الجزء الظاهر منها ليس إلا جزءًا صغيرًا من المشكلة. قبل أن نبدأ الشّرح، أنصحك بالاطّلاع على مقالة أشياء لا يمكن اعتبارها رسوما تخطيطية لتصحّح بعض المفاهيم الخاطئة الّتي قد تعلّمتها بمفردك أو ضمن شركتك. الفكرة العامة الرسوم التّخطيطية هي مُستندات تقنيّة، كالّذي في الصّورة أعلاه (ولكنّها ليست دومًا حسن المنظر كهذا!)، وهي مكوّنة من خطوط ومستطيلات وأسماء، وربّما بعض الألوان. كثيرًا ما تُقارن الرّسوم التّخطيطيّة بالمُخطّطات الهندسيّة (blueprints) لأنّهما متقاربان في الهدف. فالمُخطّط الهندسيّ يُملي على البنّائين كيفيّة إنجاز خطّة المُهندس، وليس لون الجدران أو شكل الأثاث المُفضّل، وينبغي عليهم التّقيّد بما فيها بصورة جدّيّة، فهي ليست مُجرّد "اقتراح" أو "فكرة عامّة" أو "تصوّر سريع للمشروع". أمّا الرسوم السريعة الّتي تُنجز على الألواح أو خلال جلسات العصف الذّهنيّ فهي لا تُسمّى رسومًا تخطيطيّة لأنّها فقط تضع أساسًا لإنجاز الرّسوم المطلوبة فيما بعد، وهي مع ذلك لا تزال قيّمة. قد لا يستغرق الرّسم التّخطيطيّ أكثر من ساعة، لكنّ التّخطيط له قد يطول أسابيع أو شهورًا، ومن المهمّ أن تشرح ذلك لزبائنك وزملائك في العمل. إن كان مُصمّم الواجهات أو مُطوّرها لا يستطيع استخدام رسمك التّخطيطيّ بعد، فهو إذًا ليس رسمًا تخطيطيَّا، بل تصوّرا مبدئيًّا له. قد لا يكون درسنا طويلاً، ولكنّني سأتوقّف هنا، لأنّ الدروس القادمة ستشرح كيفيّة تحسين الرّسوم التّخطيطيّة بحيث تؤدّي إلى تصميم يعمل بصورة جيّدة، وليس فقط ذا مظهر جيّد. ترجمة بتصرّف للدرس ?What is a Wireframe من سلسلة Daily UX Crash Course لصاحبها Joel Marsh. اقرأ أيضًا: النسخة العربية الكاملة من كتاب مدخل إلى تجربة المستخدم (User Experience - UX) 1.0.0 كيف تختار اللون المثالي لعلامتك التجارية دليل المبتدئين إلى التصميم التفاعلي Interaction Design كيف تصمم دعوة إلى إجراء CTA لتحويل زبائن متجرك الإلكتروني1 نقطة
-
في الآونة الأخيرة، كان هناك الكثير من التطورات في تصميم مواقع الإنترنت وهي آخذة بالانتشار يوميًا. هل أنت جزء من مجتمع مصمميّ الويب؟ إن كنت كذلك، وكنت تعمل على تطوير تصاميم لواجهات الويب، إما في مجال تطوير لوحات التحكم، أو كجزء من موقع على شبكة الإنترنت. فقد ازدادت أهمية دور مصمم الويب، و سيزداد الدور أهمية في المستقبل، نظرًا لأن العديد من الشركات الناشئة بدأت تركز على التصميم أكثر يومًا بعد يوم. قد يجد المصممون قليلو الخبرة صعوبة في التعامل مع واجهات الويب. ففي سبيل إنشاء تصميم مذهل لواجهة المستخدم، تحتاج للتركيز على العديد من العوامل. ومع ذلك، فإن السرّ يكمن في دقة التفاصيل. هل ترغب بأن تكون مصممًا ناجحًا لواجهة المستخدم؟ دعني أعلمّك. وسنبدأ مع الأساسيات. نصائح متعلقة بالبداهة كلما سهل استخدام موقعك، ارتفع عدد الزوار والعكس بالعكس. يحمل التصميم البديهي (Intuitive design) أهمية كبيرة حتى عندما نتحدث عن السهولة. وهذا يعني أن موقعك يجب أن يكون لديه طرق استخدام واضحة بمجرد أن يفتحه الزائر. عادةً ما يكون التصميم البديهي غير ظاهريًا و هذه أكبر ميزة له. لا توجد خطوط عريضة افتراضية وإنما نهج معتاد لمعظم المستخدمين يمكّنهم من فعل ما يريدون بسهولة. يساعد التصميم البديهي الأشخاص في التركيز على جودة التجربة. كل زائر يرغب في إكمال مهمة دون أن يخسر وقته في المقاطعات. التصميم غير البديهي مُقلق ومشوّش. لا يمكن رؤية التصميم البديهي ولكن هذا لا يعني أن الزوار ليسوا على دراية بوجوده. وسيلاحظون أن جميع العناصر ترتبط بالعمل الذي يقومون به، مما سيجعلهم سعداء بذلك. مبادئ تصميم واجهات المستخدم هناك ثلاثة مبادئ تحدد استخدام اللغة المرئية: التواصل - يظهر بطريقة مألوفة للمستخدمين التنظيم - تقديم بنية متناسقة ودقيقة الاقتصاد - يستخدم سلسلة من التوجيهات بكفاءة طبيعة تصميم واجهة المستخدم الهدف الرئيسي من واجهات المستخدم هو تقديم تفاعل نوعي، وبالتالي، إتاحة تجربة مستخدم مذهلة. إذا رغب المستخدمون بإكمال مهمتهم بسهولة،فلا بد أن يتعاملوا مع تصميم فعال ومباشر. أهمية تصميم واجهة المستخدم بالنسبة للتطوير في بعض الأحيان، يعقد أصحاب العمل اجتماعات لمناقشة ردود أفعال المستخدمين وإيجاد طرق لتطبيق ملاحظاتهم على التصميم. والنتيجة هي تجربة مستخدم مذهلة بسبب مزيج من البساطة والفعالية. ما الدور الذي تلعبه واجهة الويب أثناء التطوير؟ يتم إجراء بحث معتمد على احتياجات المستخدم وتفضيلاته. يناقش أصحاب الشركة كل منها للتوصل إلى تطبيق الممكن منها. من المهم الإصغاء إلى احتياجات العملاء والتغذية الراجعة منهم لضبط التصميم وفقًا لما سبق، آخذين بالحسبان أفضل ممارسات تصميم واجهة المستخدم. قواعد تصميم واجهة المستخدم قاعدة الوضوح - يتوقع المستخدمون رؤية عناصر واجهة واضحة وبسيطة. يتجنب الأشخاص المحتوى المعقد عندما يكونون على الإنترنت. كما أنهم لا يريدون قضاء الوقت في تعلم كيفية القيام بما يُفترض أنه عمل بسيط. قاعدة الأولوية - سيشعر المستخدمون بالثقة عندما يكون لديهم فكرة عما هو متوقع منهم. قاعدة السياق - يجب أن تتيح واجهة المستخدم للمستخدمين قدرة التحكم بما يرون بأنه يجب عليهم التحكم فيه. قاعدة الوضع الافتراضي - إن كان موقعك واضحًا، فلن يحاول المستخدمون تغيير الإعدادات الافتراضية. من المهم أن يكون لديك وضع افتراضي: تأتي أجهزة التلفزيون مع إعدادات افتراضية والتي نادرًا ما يتم تغييرها. نادرا ما يتم تغيير درجة حرارة الثلاجة نجد الأوضاع الافتراضية في كل جانب من جوانب الحياة الأوضاع الافتراضية جوهرية لكل تجربة تأكد من أن الأوضاع الافتراضية عمليّة وقابلة للتطبيق. فنادرًا ما يتم تغييرها. نصائح إنشاء واجهة جديدة معرفة المستخدمين: فرّق بين أهدافهم وأهدافك. أدرجها ضمن قائمة الأولويات. بعد ذلك، خُض في خبراتهم واختصاصاتهم لمعرفة ما يحتاجونه. تعرف على الواجهات المفضلة لديهم واستخدمها. تجنب الصيحات الحديثة وخصائص التصاميم التي بالكاد تمّ طرحها . الطريقة الوحيدة لمساعدة عملائك على إنجاز مهامهم هي بالتركيز عليهم. التنقُّل وهيكل تصميم واجهة المستخدم: حاول التوصل إلى حلول تركز على التفاعل بين المستخدمين والمنتج وتساعد المستخدمين على إنجاز مهمتهم. صنّف الحلول وفقا للأهمية والقوة ودور المستخدم وبالاعتماد على سهولة الاستخدام. اعتنِ بالهيكل، وبنية البيانات، والتنقُّل وصمم واجهة المستخدم تصميمًا عمليًّا مع محتوى غني. إعداد الوثيقة النهائية: يجب أن تحتوي الوثيقة النهائية على بنية واجهة المستخدم بأكملها. ولا بد أن تعرض نهج المنتج من مرحلة التهيئة إلى المرحلة النهائية عندما يتم عرضها على المتصفح. اتبع الأنماط: يصادف المستخدمون معظم الأوقات واجهات مختلفة عن الواجهة الخاصة بك. بعض المواقع التي يصادفها معظم المستخدمين يوميًا هي الفيسبوك و تويتر ومواقع الأخبار و الووردبريس. ربما كنت تعرف مدى نجاح هذه الواجهات لذا يتوجب عليك ألّا تعيد اختراع العجلة. بدلا من ذلك، استعن بتلك المنصات للبحث عن حلول للمشاكل التي تواجهها. فمن الجيد أن تمتلك أنماط مألوفة لواجهة المستخدم. ابحث في التعليقات: يجب عليك دومًا الاستجابة لاحتياجات المستخدمين. يجب أن تمنح المستخدمين الإرشادات وتفسّر سوء الفهم وتصحح الأخطاء. تأكد من إعلام المستخدمين بأي تغييرات. إبقائهم كذلك سيشعرهم كما لو أنهم جزء من عملية التغيير. في نهاية المطاف، وستكون واجهتك هي ما يريده المستخدمون. جعل واجهات الإنترنت سهلة التعلم كلما كان التفاعل أبسط مع واجهة المستخدم، سهل على المستخدم تذكر وظائفها. وهذا يعني أنك تحتاج إلى تصميم واجهة بطريقة تتيح للمستخدم أن يمتلك عددًا قليلًا من الأمور ليفعلها. وللقيام بذلك، سيتوجب عليك تبسيط المعلومات إلى أجزاء صغيرة قابلة للفهم. يجب عليك أيضًا مراعاة عرض ميزات الواجهة على الشاشة. لا تقذفها في وجوه المستخدمين، بل فكر في إضافتها إلى مكان ما على الشريط الجانبي، أو في الجزء السفلي من الشاشة لتجنب تشتيت انتباههم. جعل واجهات الويب بديهية إن أهم عامل يحدد أداء تطبيق الويب هو واجهة المستخدم. هل لديها تصميم بسيط للوحة التحكم؟ إن لم تكن كذلك، فحاول تبسيطها. يمكن لواجهة المستخدم أن تظهر أساليب بسيطة لتحقيق النتائج. لا يهم إذا كان لديك برامج قوية، فالناس تتجاهل تصميم لوحة التحكم عديم الفعالية. ما يهم هو تفاعل المستخدمين مع البرنامج أثناء مساعدتهم على تحقيق هدفهم. ولذلك، فتصميم المواقع الإلكترونية قائم على التركيز على المستخدم أكثر فأكثر. اجعل قرارات المستخدم بسيطة كل شيء يبدأ بجعل التصميم بسيطًا قدر الإمكان. لماذا؟ التصاميم المعقدة تشوش المستخدمين وتصعّب عليهم اتخاذ القرار. ما الذي يمكنني النقر عليه؟ أين؟ هل هو الزر الأحمر هنا؟ هل هو الزر الأخضر هناك؟ طبقّ هيكلًا بصريّا. ما هي أهم الأشياء في واجهتك؟ أبرزها لينصب تركيز المستخدمين عليها. يمكنك التلاعب بالحجم والألوان، والطباعة، والمساحة البيضاء، وغيرها. نمط الخط ربما كنت تقول "حسنًا، أنا أعرف نمط خطوط رائع". وهذا لا يكفي. فوجود خط جيد في تصميمك يصبح عديم الفائدة إن كنت لا تعرف كيفية استخدامه، أو أين تستخدمه، أو ما هو الخط الذي يمكنك استخدامه إلى جانبه. وضع خطوط فريدة في كل مكان لن يحميك، إذ كل خط له صفاته الخاصة ويصلح لجمهور معين. بالإضافة إلى اختيار الخط المناسب لجمهورك، يجب عليك أيضًا التأكد من استخدام حجمه المناسب، لتسليط الضوء على أجزاء معينة من التصميم. يجب أيضًا استخدام الألوان، ولكن كن حذرًا مع مجموعات ألوان معينة. الخلاصة التصميم الجيد للواجهة يُشبه الهواء الذي نتنفسه. لا يمكننا أن نرى أو نفكر فيه. ومع ذلك، فهو يلبي احتياجاتنا. إذا كنت قد صادفت سابقًا واجهة المستخدم سيئة، فستقدّر الموقع الذي يملك واجهة مستخدم سهلة. يجب على التصميم الجيد أن يُشعرك بالثقة دائمًا طالما كنت تركز على مهمتك ويقضي على القلق من أن ارتكابك للخطأ. يجب أن تكون التطبيقات ومواقع الويب عملية ويجب على المصممين وضع المزيد من الجهد والوقت على سهولة استخدام المنتج. ترجمة -بتصرف- لمقال Designer’s Guide for Designing Web App Interfaces لصاحبه Iggy1 نقطة
-
سأقدِّم لك أداةً رائعةً للتصميم: أرأيت؟ ألم ترَ؟ إنها الفراغات البيضاء! حسنًا، ربما مزاحي لم يكن في محلّه، أعترف بذلك. أنا آسف. صحيحٌ أنَّ تعريفي للفراغات البيضاء لك لم يكن مثاليًا، لكن الفراغات البيضاء هي من أهم الأدوات في تصميم الويب، ولكن يغفل عنها الناس معظم الوقت. أعلمُ أنَّ من الممتع العمل على عناصر أخرى من تصميم الويب، مثل سمة الألوان، أو الخطوط، أو التخطيط العام للصفحة… خصوصًا عندما تُنشِئ مشروعًا لعميلٍ ما (ملاحظة شخصية: لا أظن أنَّ هنالك عميلًا في العالم يلقي بالًا لطريقة تحسين الفراغات البيضاء لموقعه الجديد!). ومع ذلك، قد تستفيد من الفراغات البيضاء خيرَ استفادة إن استطعت استخدامها استخدامًا صحيحًا. وسأتطرّف في هذا الدرس إلى آلية فعل ذلك. لماذا الفراغات البيضاء مهمة في تصاميم الويب بدايةً، الفراغات البيضاء ليست مفهومًا جديدًا في عالم التصميم، إذ استعمِلَت لقرون كالأداة رقم 1 لإعطاء تركيز على العناصر المهمة في التصميم. والأمر سيانٌ في يومنا هذا. على سبيل المثال، إذا كانت لديك لوحةٌ جميلةٌ وتريد أن تُظهِر اهتمامك بها، فإن أفضل طريقة هي: (1) وضع إطار حولها، و (2) ألّا تضع أيَّ شيءٍ آخر على ذاك الجدار (انظر الصورة أدناه). وبشكلٍ مشابه، أفضل طريقة لإعطاء أولوية لأحد عناصر صفحة الويب هي عدم وضع أي شيء حوله، وكلما قلَّت الأشياء التي حول ذاك العنصر، كلما كان ذلك أفضل. ما رأيك أن ترى مثالًا بدلًا من إطالة الكلام (الصورة خيرٌ من ألف كلمة). هذا موقعٌ تدخل إليه يوميًا: يستعمل موقع Google هذا المظهر منذ سنوات، وربما أصبح شكله مألوفًا، لهذا قد لا تفكّر في تصميم الصفحة كثيرًا. لكن دعنا نتوقف قليلًا ونفكِّر كم أنَّ من السهل أن تضع Google بعض الأشياء الإضافية في الصفحة. إذ يستطيعون تضمين الأخبار (من Google News) وسيكون بعض الأشخاص سعداء بذلك. ويستطيعون أيضًا تضمين مربع لبريد Gmail لكي يتمكن الجميع من التحقق من الرسائل التي تأتيهم على بريدهم مباشرةً. أو أن يضعوا مربعًا لتقويم Google، وهلم جرًا… الاحتمالات والإمكانيات غير محدودة، إلا أنَّ Google قرروا عدم وضع أيّا من تلك الاحتمالات في الصفحة الرئيسية؛ إذ قرروا أنَّ يضعوا حقل البحث فقط (بالإضافة إلى الشعار، وبعض الأشياء في الركن العلوي الأيمن إن سجَّلتَ دخولك). لكن لماذا؟ لماذا وضعوا حقل البحث فقط؟ الجواب بسيطٌ للغاية، وإنّي واثقٌ أنَّك تعرفه مسبقًا: بوضع حقل البحث في الصفحة بمفرده، فسيظهر الغرض من الصفحة جليًا أمامك. فسيعلم زائر الصفحة بعد دخوله إليها مباشرةً ما الغرض منها وكيف يستعملها، فلا يضيع وقته بمحاولة «فهم» ما الذي يجري في الصفحة. وهذا يتوافق تمامًا مع هدف Google الرئيسي. يريدونك أن تستعمل محرك البحث الخاص بهم، وتُشكِّل الفراغات البيضاء أحد الأشياء التي يستعملها مطورو Google لكي يحثوك على فعل ذلك. لتلخيص ما سبق، يمكن للفراغات البيضاء المُستعمَلة استعمالًا صحيحًا أن: تساعد في حثّ المستخدم على القيام بأمرٍ معيّن تساعد على التركيز على أحد العناصر تساعد في توضيح الغرض من الموقع تعطي تركيزًا على الأشياء المهمة تجعل الأشياء التي ليست مهمة جدًا بأولوية منخفضة تساعد الزائر على المسح البصري للصفحة وتقرير ما الذي يهمه مباشرةً سنتحدث الآن عن كيفية استعمال الفراغات البيضاء بفعالية، بعد بأخذ كل ما سبق بعين الاعتبار. كيف تستعمل الفراغات البيضاء بفاعلية في تصاميم الويب لنتحدث عن الفراغات البيضاء من ناحية منهجية التعامل معها، وبعض المعلومات التقنية الأساسية لكيفية إنشاءها (لكنا لن نشرح الأدوات والطرق المستعملة لذلك). أولا: استعمل الفراغات البيضاء لتدعيم الهدف الأساسي من موقعك حسنًا، هنالك هدفٌ من إنشاء كل صفحةٍ أو كل موقعٍ على الويب. ومن الممكن أن يكون هنالك هدفٌ وحيدٌ لكل صفحات الموقع، أو أن يكون لكل صفحةٍ هدفٌ خاصٌ بها. أيًّا كان، يمكنك أن تستعمل الفراغات البيضاء لجعل تلك الأهداف واضحةً كالبدر في الليلة الصافية. على سبيل المثال، لنلقِ نظرةً على الصفحة الرئيسية لموقع Bigcommerce: من الواضح أنَّ الهدف الرئيسي من الصفحة هو إقناعك -أي الزائر- أن تُسجِّل تجريبيًا في Bigcommerce. وفي الواقع، لا يوجد أيٌ شيءٍ في الصفحة سوى عنوان بخطٍ كبير الذي يحاول إقناعك، بالإضافة إلى فراغات بيضاء كبيرةٍ حوله. حسنًا، تُمثِّل الصورة مثالًا عن المنتج، لكنها موجودةٌ في مركز الصفحة، مما يزيد في إضفاء الأهمية على العنوان. لم يسبق لي التّعامل مع Bigcommerce، إلا أنني متأكدٌ أنَّهم يستطيعون إضافة أشياءٍ كثيرةٍ في الصفحة الرئيسية؛ إلا أنَّهم قرروا عدم فعل ذلك. لماذا؟ لتدعيم الغرض الرئيسي من الصفحة. ثانيا: استعمل الفراغات البيضاء للحث على القيام بإجراء معين هذه إحدى قواعد التصميم الجيد للويب: افترض دومًا أنَّ الزائر لا يعرف ماذا عليه أي يفعل في الخطوة القادمة. أنَّى لهم أن يعلموا؟ لا تنسَ أنهم لم يصمموا أو يبرمجوا الصفحة، وإنما أتو لتوهم إليها … ربما عن طريق الخطأ! استعمل الفراغات البيضاء لمساعدة الزوار ليعلموا ماذا عليهم أن يفعلوه. الفكرة بسيطة: إذا لم يكن هنالك الكثير من الأشياء في الصفحة فيمكن للزائر أن يتفاعل مع الأشياء القليلة المتبقية في الصفحة. هذا مثالٌ من Crazy Egg: قد نعتبر أنَّ الصفحة السابقة فارغة تقريبًا، إذ أنَّ التصميم شبيهٌ جدًا بتصميم صفحة Google الرئيسية؛ لكنه يوضِّح ماذا يستطيع أن يفعل المستخدم. حتى لو لم تتعامل مع Crazy Egg من قبل، فيمكنك أن تعرف ماذا عليك أن تفعل بسرعة. حيث يوجد حقل إدخالٍ فيه تلميحة ("Your website URL") وزر يقول "Show Me My Heatmap". الغرض من هذه الصفحة واضحٌ ألا وهو حثّ المستخدم على اتخاذ إجراء، وتساعد الفراغات البيضاء بذلك، وتجعل الصفحة أكثر "نظافةً". ثالثا: ليس من الضروري أن تكون الفراغات البيضاء "بيضاء" ربما هذه اللحظة مناسبة لكي أصحِّحَ لبسًا في المفاهيم: ليس من الضروري أن يكون لون الفراغات البيضاء هو اللون الأبيض. بأبسط الكلمات، الفراغات البيضاء تعني عدم وجود أي عناصر ذات محتوى، ولا تعني أنَّ تلك الفراغات لونها أبيض. بهذا الخصوص، تستطيع استخدام مختلف العناصر كفراغات بيضاء. يمكنك أن تستعمل الألوان التي تحبها، على سبيل المثال Marshall: أو يمكنك أن تستعمل خلفية مشوشة (blurred) أو نقوش. مثالٌ من Zapier: في النهاية، يمكنك أن تخاطر باستخدام صور غير مشوشة لطالما كان هنالك تباينٌ كبيرٌ بينها وبين محتوى الصفحة. يمكنك رؤية مثالٍ عمليٍ عنها في موقع Grammarly: رابعا: استخدم الفراغات البيضاء في القسم العلوي للصفحة شاعت في الآونة الأخيرة ما نسميه أقسام hero (hero sections) أو صور hero (hero images) التي هي الأقسام التي تظهر في أول الصفحة والتي تسترعي انتباه الزوار. ترويسة الموقع أو قسم hero هو المكان الملائم لاستخدام الفراغات البيضاء. حيث ستجد أنَّه من السهل جدًا استعمال الفراغات البيضاء في تلك الأماكن، وستعطيك مكانًا رائعًا للتحدث فيه عن خدمتك التي تقدمها. الذي أقصده هو أنَّ هذا القسم في أعلى الصفحة وسيراه كل زائر… حيث لا يمرر إلى أسفل الصفحة إلا القليلون، بينما سيرى جميع الزوار القسم العلوي منها. لذا ركِّز على هذا وتأكّد أن تستغل الفراغات البيضاء جيدًا في هذا القسم. على سبيل المثال، إحدى صفحات موقع Teespring: سنرى الكثير من العناصر في القسم الذي يلي قسم hero مع فراغات بيضاء قليلة. إلا أنَّ المنطقة العلوية تقوم بعملها على أتم وجه بدعم بتدعيم الهدف الرئيسي من الصفحة وتوضيح الأمور للزائر. خامسا: استخدم الفراغات البيضاء للتلاعب بالمشاعر هنالك الكثير من الأدوات بحوزتك إن كنت تسعى خلف التأثير عاطفيًا على الزائر فيمكنك أن تستعمل الألوان، أو صورة جيدة، أو قد تستفيد من الفراغات البيضاء! جميع العناصر السابقة لها دورٌ عندما يأتي الأمر إلى إنشاء استجابة عاطفية، لكن الفراغات البيضاء تضفي لمسةً من «الدراما» إلى الموقف. فبنفس الطريقة التي تستطيع الفراغات البيضاء تدعيم هدف الصفحة، ستستطيع أن تدعِّم العاطفة التي تود التأثير عليها عند الزائر. لننظر إلى هذا المثال من Todoist: Todoist هو أداة لإنشاء قائمة بالمهام. لكن باستخدامهم للمسافات البيضاء استخدامًا جيدًا حول العنوان الرئيسي، جعلوا الصور بارزةً وأضفت بعض المشاعر الإيجابية. فبدلًا من عرض صورة للمنتج نفسه، عرضوا صورةً لشخصٍ سعيدٍ يبدو أنَّه يستمتع بيومه، بعد أن أنهى مجموعةً من المهام الموكلة إليه. سادسا: حارب نزعة المصمم لملء الفراغات حسنًا، نحن البشر نحب أن نملأ الفراغات؛ فعندما نرى مكانًا فارغًا، سنفكر -لا إراديًا- كيف نستطيع أن نملأه. لكن هذه العقلية -الطبيعية جدًا- قد تسبب مشكلةً للمصمم. فعلى عكس رفوف المكتبة، ليس عليك في تصميم الويب أن تطمح إلى ملء جميع الفراغات. لذا ابدأ هكذا: ضع هدفًا للصفحة واختر عنصرًا وحيدًا يستطيع تحقيق ذاك الهدف؛ الذي يمكن أن يكون «عنوانًا رئيسيًا + دعوةً إلى إجراءٍ ما». ضع ذاك العنصر في منتصف التصميم. ضع فراغات حوله. أضف عناصر أخرى حول تلك الفراغات. سابعا: فكر بما تستطيع حذفه، لا بما تستطيع إضافته هذه النقطة مرتبطةٌ بالنقطة التي تسبقها؛ لكن بالمقلوب. بعد أن تنتهي من تصميمك، قد تشعر أنَّ هنالك أشياءً كثيرةً هنا وهناك، وستبدأ بالتفكير بالعناصر التي تستطيع حذفها من الصفحة والتي لا تؤثر تأثيرًا سلبيًا على الهدف الرئيسي. فكلما قللت العناصر، كلما كان ذلك أفضل. ويمكنك أن تعتبر أنَّ تصميمك كاملٌ إن لم يبقَ شيءٌ تستطيع حذفه، لا إضافته. ثامنا: كلما كبر حجم الخط، كلما احتجت إلى فراغ أكبر العناوين الكبيرة أصبحت "موضة" في هذه الأيام، وخصوصًا في عصر التصميمات المسطحة والعقلية السائدة المؤيدة لها. لكن الخطوط الكبيرة تحتاج إلى مساحة للتنفس؛ فإن لم تكن هنالك فراغات كافية حول العناوين الضخمة في تصميمك، فستخسر قوتها وستبدو كأنها صعبة القراءة… بغض النظر أنَّها لم تعد فعالةً. لذا ستكون القاعدة كالآتي: استخدم نصًا كبيرًا إذا ما استطعت توفير فراغات بيضاء كبيرة حوله. انظر هذا المثال من Dior: لاحظ حجم الخط الكبير جدًا للعنوان، وكمية الفراغات البيضاء التي حوله. الفراغات البيضاء على الهواتف المحمولة؟ كن حذرًا هنا. صحيحٌ أنَّ القواعد والمبادئ العامة تنطبق على تصاميم مواقع الهواتف المحمولة، ومن المحتمل أن تستفيد من الفراغات البيضاء فيها؛ إلا أنَّ هنالك حدًا دقيقًا فاصلًا بين "الاستخدام الجيد للفراغات البيضاء" وبين "ترك فراغات كثيرة بين العناصر". يكون الحد الفاصل عادةً هو عدِّة بكسلات في أحد الاتجاهات، ومن السهل جدًا أن تنتقل من تجربةٍ رائعةٍ للمستخدم إلى واجهةٍ صعبة التصفح نتيجةً لوجود الكثير من الفراغات. أرى أن تحاول جعل كل كتلة من المحتوى المهم (مثل العنوان + عبارة لحث المستخدم على اتخاذ إجراء) تملأ شاشة الهاتف بأكملها، وإن كانت هذه المهمة صعبةً في بعض الأحيان. على سبيل المثال، انظر إلى هذا التصميم من Evernote عندما يُعرَض على هاتفٍ محمول: ستُعرَض الكتلة الرئيسية من المحتوى على كامل الشاشة، ومن ثم سيبدأ المستخدم بالتمرير إلى الأسفل ليرى ماذا تحتوي بقية الصفحة. النقطة الصعبة هنا هي أنَّه عليك أن تتعامل مع أجهزةٍ مختلفةٍ، وفي حين أنَّك تستطيع اختبار التصميم على أكثر الأجهزة شيوعًا، إلا أنَّه من المستحيل أن يبدو تصميمك بشكلٍ صحيح على كل الأجهزة. أضف إلى ذلك أنَّك تريد أن يُعرَض عرضًا سليمًا على الحواسيب أيضًا؛ مما يزيد التعقيد كثيرًا. الرسالة الرئيسية التي أريد أن أوصلها لك هنا هي أن تبقى حذرًا، والزم المبادئ العامة لآلية استغلال الفراغات البيضاء دون الإفراط باستخدامها، وسيكون الأمر على ما يرام. ترجمة -وبتصرّف- للمقال The Importance of Whitespace in Web Design لصاحبه Karol K.1 نقطة