الجلسات HTTP في Laravel
مقدّمة
توفر الجلسات طريقة لتخزين المعلومات حول المستخدم على عدّة طلبات نظرًا لأن التطبيقات المُعتمدة على HTTP بدون حالة. يأتي Laravel مع مجموعة متنوعة من الخلفيّات (backends) التي يمكن الوصول إليها عبر واجهة برمجية API تعبيرية موحّدة. كما يتضمّن دعم خلفيات شائعة مثل Memcached وRedis وقواعد البيانات خارج الأطر المألوفة.
الضبط
يُخزّن ملف إعدادات الجلسة في config/session.php
. تأكد من مراجعة خياراتك المتاحة في هذا الملف. Laravel مُعد افتراضيًّا لاستخدام برنامج تشغيل (driver) الجلسة file الذي يعمل جيّدًا في العديد من التطبيقات. قد تفكر في تطبيقات الإنتاج في استخدام برامج التشغيل Memcached أو Redis للحصول على أداء جلسة أسرع.
يُعرّف خيار إعداد الجلسة driver مكان تخزين بيانات الجلسة لكل طلب. يأتي Laravel مع عدّة برامج تشغيل ممتازة خارج الأطر المألوفة:
- file - تُخزّن الجلسات في storage/framework/sessions .
- cookie - تُخزّن الجلسات في ملفات تعريف الارتباط آمنة ومشفّرة.
- database - تُخزّن الجلسات في قاعدة بيانات علائقية.
- memcached / redis - تُخزّن الجلسات في إحدى هذه المخازن السريعة القائمة على ذاكرة التخزين المؤقت.
- array - تُخزّن الجلسات في مصفوفة PHP ولن تستمر.
يُستخدم برنامج تشغيل المصفوفة أثناء الاختبار ويمنع استمرار تخزين البيانات المخزّنة في الجلسة.
متطلبات المُشغّل
قاعدة البيانات
ستحتاج لإنشاء جدول لاحتواء عناصر الجلسة عند استخدام برنامج تشغيل قاعدة بيانات الجلسة. في ما يلي مثال عن تصريح Schema للجدول:
Schema::create('sessions', function ($table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
});
تستطيع استخدام الأمر session:table Artisan
لتوليد هذا التهجير (migration):
php artisan session:table
php artisan migrate
Redis
ستحتاج إلى تثبيت الحزمة predis/predis (~ 1.0) عبر Composer قبل استخدام جلسات Redis مع Laravel. تستطيع إعداد اتصالات Redis في ملف الإعداد database. يمكن استخدام الخيار connection
لتحديد أي اتصال Redis تستخدمه الجلسة في ملف الإعداد session.
استخدام الجلسة
استرداد البيانات
توجد طريقتان أساسيّتان للعمل ببيانات الجلسات في Laravel: عبر المساعد session
العام وعبر نسخة Request
. فلنلقي نظرة أولًا على الوصول للجلسة عبر نسخة Request
والتي يمكن التلميح على نوعها على تابع وحدة التحكّم. تذكر أنّ اعتماديّات تابع وحدة التحكّم تُضاف تلقائيًا عبر حاوي خدمات Laravel:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* عرض ملف المستخدم المحدّد الشخصي .
*
* @param Request $request
* @param int $id
* @return Response
*/
public function show(Request $request, $id)
{
$value = $request->session()->get('key');
//
}
}
تستطيع عندما تسترد عنصرًا من الجلسة تمرير قيمة افتراضية كالمتغيّر الوسيط الثاني للتابع get
. ستُرد هذه القيمة الافتراضية إن لم يوجد المفتاح المحدّد في الجلسة. إن مرّرت Closure كقيمة افتراضية للتابع get
ولم يُعثر على المفتاح المطلوب سيُنفّذ Closure وترد نتيجته:
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
مساعد الجلسة العام
تستطيع أيضًا استخدام الدالّة العامّة session
لاسترداد البيانات وتخزينها في الجلسة. سيرد المساعد session
، عند مناداته بمتغيّر سلسلة نصيّة وسيط واحد، قيمة مفتاح تلك جلسة. عند مناداة المساعد مع مصفوفة من أزواج المفاتيح / القيم ستُخزّن تلك القيم في الجلسة:
Route::get('home', function () {
// جلب قطعة بيانات من الجلسة...
$value = session('key');
//تحديد قيمة افتراضيّة...
$value = session('key', 'default');
// تخزين جزء البيانات في الجلسة...
session(['key' => 'value']);
});
ملاحظة: بالكاد يوجد أي اختلاف عملي بين استخدام الجلسة عبر نسخة طلب HTTP مقابل استخدام المساعد العام session
. كلتا الطريقتين قابلتان للاختبار عبر التابع assertSessionHas
المتوفر في جميع حالات اختبارك.
استرداد جميع بيانات الجلسة
إن رغبت في استرداد جميع بيانات الجلسة تستطيع استخدام التابع all
:
$data = $request->session()->all();
تحديد وجود عنصر في الجلسة
تستطيع استخدام التابع has
لتحديد وجود عنصر في الجلسة. يرد التابع has
القيمة true
إن وجد العنصر ولم يكن فارغًا (null):
if ($request->session()->has('users')) {
//
}
تستطيع استخدام التابع exists
للتأكد من وجود العنصر في الجلسة حتى إن كانت قيمته null
. يعيد التابع exists
القيمة true
إذا كان العنصر موجودًا:
if ($request->session()->exists('users')) {
//
}
تخزين البيانات
ستستخدم عادةً التابع put
أو المساعد session لتخزين البيانات في الجلسة:
// عبر نسخة طلب...
$request->session()->put('key', 'value');
//عبر مساعد عام...
session(['key' => 'value']);
إضافة العناصر إلى مصفوفة الجلسة (Pushing To Array Session Values)
يمكن استخدام التابع push
لدفع قيمة جديدة على قيمة جلسة مصفوفة. إن احتوى المفتاح user.teams
مثلًا مصفوفة من أسماء الفرق تستطيع دفع قيمة جديدة لداخل المصفوفة كما يلي:
$request->session()->push('user.teams', 'developers');
استرداد وحذف عنصر
سيستردَ التابع pull
ويحذف العنصر من الجلسة بسطر برمجي واحد:
$value = $request->session()->pull('key', 'default');
تمويض البيانات (Flash Data)
قد ترغب أحيانًا في تخزين العناصر في الجلسة لكن للطلب التالي فقط. يمكنك ذلك باستخدام التابع flash
. ستُتاح البيانات المخزنة في الجلسة باستخدام هذا التابع فقط أثناء الطلب HTTP التالي وتُحذف بعد ذلك. تفيدنا بيانات Flash
بصفة عامّة برسائل الحالة قصيرة الأمد:
$request->session()->flash('status', 'Task was successful!');
إن احتجت للاحتفاظ ببياناتك المموّضة (Flash) لعدة طلبات، تستطيع استخدام التابع reflash
الذي سيحتفظ بجميع بيانات Flash لطلب إضافي. إذا احتجت فقط للاحتفاظ ببيانات Flash محددة، تستطيع استخدام التابع keep
:
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
حذف البيانات
سيحذف التابع forget
جزءًا من البيانات من الجلسة. تستطيع استخدام التابع flush
إن رغبت في إزالة جميع البيانات من الجلسة:
$request->session()->forget('key');
$request->session()->flush();
تجديد مُعرّف الجلسة (Regenerating The Session ID)
غالبًا ما يُجدّد مُعرّف الجلسة لمنع المستخدمين الخبثاء من استغلال هجوم تثبيت جلسة (session fixation) ضد تطبيقك.
يعيد Laravel إنشاء معرّف الجلسة تلقائيًا أثناء الاستيثاق (authentication) إن كنت تستخدم LoginController
؛ ولكن إن احتجت لإعادة إنشاء معرّف الجلسة يدويًا تستطيع استخدام التابع regenerate
.
$request->session()->regenerate();
إضافة برامج تشغيل جلسة مخصّصة
تعريف استخدام برنامج تشغيل
يجب أن يُعرّف برنامج تشغيل جلستك المخصّص استخدام SessionHandlerInterface
. تحتوي هذه الواجهة على بعض التوابع البسيطة التي نحتاج لتعريف استخدامها. تبدو بذرة تطبيق MongoDB هكذا:
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
ملاحظة: لا يأتي Laravel مصحوبًا بمجلّد يحتوي على إضافاتك. أنت حر في وضعها في أي مكان تريده. أنشأنا في هذا المثال مجلّد Extensions لإيواء MongoSessionHandler.
بما أنّ الغرض من هذه التوابع ليس بديهيَّ الفهم، دعنا نغطي بسرعة ما يفعله كل تابع منها:
- عادةً ما يُستخدم التابع
open
في أنظمة تخزين الجلسات القائمة على الملفات. لن تحتاج أبدًا تقريبًا لوضع أي شيء في هذا التابع نظرًا لأن Laravel يُشحن مع برنامج تشغيل جلسة file. تستطيع تركها كبذرة فارغة. في الواقع اشتراط تعريف استخدام هذا التابع نابع من ضعف تصميم واجهة PHP (الذي سنناقشه لاحقًا) - يمكن عادةً تجاهل التابع
close
مثل التابعopen
. فلا حاجة إليه بالنسبة لمعظم برامج التشغيل. - يجب أن يرد التابع
read
نسخة السلسلة نصيّة لبيانات الجلسة المرتبطة بالمتغيّر $sessionId
. لا داعي لأي سَلسَلة (serialization) أو ترميز (encoding) عند استرداد أو تخزين بيانات الجلسات في برنامج تشغيلك بما أنّ Laravel سيهتّم بالسلسلة مكانك. - يجب أن يكتب التابع write السلسلة النصيّة $data المحدّدة المرتبطة بـالمتغيّر
$sessionId
بنظام تخزين مستمر (persistent storage) مثل MongoDB أو Dynamo إلخ. للتذكير مجددًّا لا ينبغي أن تُسلسل أي شيء - بما أن Laravel يهتم بذلك بدلًا منك. - يجب أن يحذف التابع
destroy
البيانات المرتبطة بـالمتغيّر $sessionId
من التخزين الدائم. - يجب أن يدمّر التابع gc كافة بيانات الجلسة الأقدم من المدّة
$lifetime
المحدّدة وهي طابع زمني UNIX. بالنسبة للأنظمة التي تُنهي صلاحيتها (self-expiring) مثل Memcached و Redis ، يمكن ترك هذا التابع فارغًا.
تسجيل برنامج التشغيل
تستطيع تسجيل برنامج تشغيلك في إطار العمل بمجرد تعريف استخدامه. لإضافة برامج تشغيل أخرى لخلفيّة جلسة Laravel، تستطيع استخدام التابع extend
على الواجهة الساكنة Session. عليك بمناداة التابع extend
من تابع موفّر الخدمات boot
. تستطيع فعل بذلك من AppServiceProvider
الموجود أو إنشاء موفّر خدمات جديد:
<?php
namespace App\Providers;
use App\Extensions\MongoSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
class SessionServiceProvider extends ServiceProvider
{
/**
* إجراء تمهيد الخدمات ما بعد التسجيل.
*
* @return void
*/
public function boot()
{
Session::extend('mongo', function ($app) {
// Return implementation of SessionHandlerInterface...
return new MongoSessionHandler;
});
}
/**
* تسجيل الارتباطات في موفّر الخدمات.
*
* @return void
*/
public function register()
{
//
}
}
تستطيع استخدام برنامج التشغيل mongo بملف إعدادك config/session.php
بمجرد تسجيل برنامج تشغيل الجلسة.