اسکریپت سفارشی PHP

ایجاد اسکریپتی که به‌طور مستقیم فریم‌ورک CMS را لود و اجرا (بوت) کند، توصیه نمی‌شود و فقط در موارد بسیار خاص و نادر لازم است. بسته به نوع کاربرد، بهتر است یک پلاگین کنسول ایجاد کنید و برنامه کنسول جوملا را توسعه دهید. اگر قصد دارید یک نقطه ورود ساده برای درخواست‌های وب بسازید، بهتر است یک پلاگین ajax بنویسید.

مقدمه

منظور از اسکریپت سفارشی PHP، اسکریپتی است که بتوان از طریق مرورگر اجرا کرد. معمولاً کاربران با مرورگر به سایت جوملا مراجعه می‌کنند:

https://example.com/index.php

یا به بخش مدیریت جوملا:

https://example.com/administrator/index.php

در این بخش قصد داریم درباره‌ی توسعه‌ی یک اسکریپت PHP (مثلاً myscript.php) صحبت کنیم که در ریشه سایت قرار می‌گیرد و کاربران می‌توانند به آدرس زیر مراجعه کنند:

https://example.com/myscript.php

این روش با گزینه امنیتی پیشرفته‌ی Public Folder که در جوملا ۵ معرفی شده است، کار نمی‌کند.

روش‌های استفاده از فریم‌ورک جوملا برای انجام کارهای موردی (ad hoc):

- استفاده از برنامه کنسول جوملا (Joomla Console Application)

- استفاده از زمان‌بندی‌ کارها (Task Scheduler) جوملا

- استفاده از کامپوننت Ajax جوملا

- استفاده از اسکریپت سفارشی PHP

نکات اضافی

برنامه کنسول جوملا نیاز دارد که اسکریپت (cli/joomla.php) از طریق خط فرمان سرور اجرا شود (مثلاً در یک کرون جاب)، که این موضوع اجرای اسکریپت برای کاربران عادی را پیچیده می‌کند و علاوه بر آن امنیت را هم کاهش می‌دهد چون به کاربران دسترسی خط فرمان داده می‌شود.

به همین ترتیب، برای اجازه دادن به کاربران برای اجرای یک کار زمان‌بندی شده (Task Scheduler)، باید به آن‌ها دسترسی به بخش مدیریت بدهید.

بنابراین، برای کارهای ساده‌ی موردی که می‌خواهید به کاربران عادی سایت اجازه دهید اجرا کنند، استفاده از اسکریپت PHP سفارشی یک گزینه است. اما توجه داشته باشید که تیم توسعه جوملا این نوع اسکریپت را به صورت رسمی پشتیبانی نمی‌کند.

نمونه اسکریپت PHP 

رویکرد کلی

برای استفاده از فریمورک جوملا و به خصوص APIهای جوملا، نیاز به یک نمونه از کلاس Application دارید. از آنجا که ConsoleApplication و CliApplication طوری طراحی شده‌اند که فقط از خط فرمان اجرا شوند، و همچنین AdministratorApplication و ApiApplication مناسب شرایط شما نیستند، بهترین انتخاب ساخت اسکریپت بر پایه‌ی SiteApplication است.

بنابراین برای ساخت این اسکریپت باید اساساً روندی را که جوملا برای مقداردهی اولیه (initialise) و نمونه‌سازی SiteApplication انجام می‌دهد تکرار کنید که شامل موارد زیر است:

- اجرای فایل‌های آغازین (start-up) PHP جوملا

- نمونه‌سازی SiteApplication از طریق کانتینر تزریق وابستگی (Dependency Injection Container) که همزمان وابستگی‌های کلیدی آن را هم مقداردهی می‌کند.

- فراهم کردن مقادیر پیکربندی لازم به نمونه Application (مثل زبان، کاربر، ثبت رویدادها)

از آنجا که روند راه‌اندازی جوملا گاهی تغییر می‌کند، لازم است پس از هر به‌روزرسانی نسخه، مطمئن شوید اسکریپت شما کار می‌کند و ممکن است نیاز به تغییر پیدا کند.

نمونه کد اسکریپت پایه

این کد را می‌توانید مثلاً در پوشه `/cli` داخل ریشه‌ی سایت جوملای خود ذخیره کنید و اجرا کنید. بخش‌های اختیاری را می‌توانید برای تست موقت غیرفعال کنید.

 
<?php

use Joomla\CMS\Factory;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Language\Language;
use Joomla\CMS\Log\Log;

define('_JEXEC', 1);

// تعیین سطح گزارش خطا
error_reporting(E_ALL);
ini_set('display_errors', 1);

// فرض شده این فایل PHP در یک پوشه زیر ریشه جوملا مثل <root>/cli قرار دارد
define('JPATH_BASE', realpath(dirname(__FILE__).'/..'));

require_once JPATH_BASE . '/includes/defines.php';
require_once JPATH_BASE . '/includes/framework.php';

// بوت‌کردن کانتینر تزریق وابستگی (DI container) - این کد کلاس‌های داخل libraries/src/Service/Provider را بارگذاری می‌کند
$container = \Joomla\CMS\Factory::getContainer();

// ایجاد نام مستعار برای دریافت وابستگی نشست (Session) از کانتینر DI
$container->alias(\Joomla\Session\SessionInterface::class, 'session.web.site');

// نمونه‌سازی برنامه کاربردی (Application)
$app = $container->get(\Joomla\CMS\Application\SiteApplication::class);

// تنظیم مقدار بازگشتی از Factory::getApplication()
\Joomla\CMS\Factory::$application = $app;

// اختیاری - ایجاد نقشه‌ نام‌فضاهای مربوط به افزونه‌ها
$app->createExtensionNamespaceMap();

// اختیاری - تنظیم چارچوب ثبت وقایع (Logging)
if ($app->get('log_everything')) {
    Log::addLogger(['text_file' => 'everything.php']);
}
Log::add('logging initialised ok', Log::DEBUG, 'script-debug');

// اختیاری - تنظیم زبان و بارگذاری رشته‌های متنی جوملا - زبان مورد علاقه خود را انتخاب کنید
$lang = Language::getInstance("en-GB");
$app->loadLanguage($lang);
$app->getLanguage()->load('lib_joomla', JPATH_ADMINISTRATOR);

// در این قسمت کدی که می‌خواهید اسکریپت اجرا کند را قرار دهید
$db = $container->get(Joomla\Database\DatabaseInterface::class);

$query = $db->getQuery(true);
$query->select('count(*)')
    ->from('#__content');

$db->setQuery($query);

$count = $db->loadResult();

echo "<h2>{$count} articles found</h2>";

 

ورود به سیستم یا عدم ورود به سیستم

همانطور که در اسکریپت بخش قبلی دیدید، لزوماً نیازی نیست برای انجام کوئری روی پایگاه داده و نمایش نتیجه به جوملا وارد (login) شوید. اما آیا باید وارد جوملا شوید یا خیر؟ سه گزینه پیش رو دارید:

- وارد نشوید

- با استفاده از اطلاعات کاربری که مستقیماً در اسکریپت وارد (hard-code) کرده‌اید، وارد شوید

- بررسی کنید که آیا کاربر از قبل وارد شده است یا خیر

حالا این گزینه‌ها را به ترتیب از آخر به اول بررسی می‌کنیم.

بررسی ورود کاربر به سیستم

جوملا چطور متوجه می‌شود که کاربر وارد شده است؟ این کار از طریق استفاده از کوکی‌ها انجام می‌شود، همانطور که در دیاگرام زیر دیده می‌شود: 

اسکریپت سفارشی PHP

هر بار که جوملا به درخواست HTTP پاسخ می‌دهد، کوکی‌هایی برای مرورگر ارسال می‌کند که شامل یک کوکی نشست (session cookie) است. کوکی نشست حکم اشاره‌گر (pointer) به داده‌های نشست ذخیره شده روی سرور را دارد. اگر کاربر وارد سایت جوملا شود، این داده‌های نشست شامل نام کاربریی است که با آن وارد شده است.

وقتی کاربر صفحه‌ای از سایت را مشاهده می‌کند یا فرم را ارسال می‌کند، مرورگر تمام کوکی‌هایی را که قبلاً جوملا ارسال کرده بود، همراه درخواست HTTP به سرور جوملا می‌فرستد. جوملا کوکی نشست را پیدا کرده و با استفاده از آن داده‌های نشست را بازیابی و بررسی می‌کند تا ببیند که آیا کاربری وارد شده است و در این صورت نام کاربری آن چه هست.

وقتی کاربران از سایت جوملا بازدید می‌کنند، اساساً به اسکریپت **index.php** در مسیر اصلی جوملا دسترسی دارند. اما از آنجا که مرورگر کوکی‌ها را برای همه اسکریپت‌های دامنه یکسان ارسال می‌کند، وقتی کاربر به آدرس زیر می رود ، مرورگر تمام کوکی‌های مربوط به آن دامنه را با درخواست HTTP ارسال می‌کند.

`<your-domain>/cli/myscript.php`

یافتن نام کاربری کاربر وارد شده در اسکریپت شما

در اسکریپت خودتان می‌توانید کاری کنید که جوملا کوکی نشست را پیدا کند و از طریق آن داده‌های نشست را واکشی کرده و نام کاربری کاربر وارد شده را بیابد. برای این کار معمولاً از کد زیر استفاده می‌کنید:

 
$user = \Joomla\CMS\Factory::getApplication()->getSession()->get('user');

اما در اسکریپتی که مثال زده شد، قبلاً یک متغیر `$app` داریم که به نمونه‌ی Application اشاره می‌کند، بنابراین می‌توانید به شکل زیر بنویسید:

 
$user = $app->getSession()->get('user');

نتیجه یک شیء از کلاس `Joomla\CMS\User\User` خواهد بود که:

- مقدار id آن برابر با صفر است، اگر کاربر وارد نشده باشد.

- id مقداری بزرگ‌تر از صفر دارد، اگر کاربر وارد شده باشد.

همچنین سایر خصوصیات این شیء User را می‌توانید در مستندات API کاربر (User API) مشاهده کنید.

بارگذاری هویت کاربر (Identity)

اگر اسکریپت شما از کدهای کتابخانه یا افزونه‌های جوملا استفاده می‌کند، ممکن است این کدها انتظار داشته باشند که هویت کاربر از طریق متد زیر در Application قابل دسترسی باشد:

 
$app->getIdentity();

 برای اینکه این امکان فراهم شود، باید هویت کاربر را بارگذاری کنید:

 
$app->loadIdentity($user);

با این کار، یک اشاره‌گر (pointer) به شیء User در داخل شیء Application ذخیره می‌شود و کدهای دیگر می‌توانند به آن دسترسی داشته باشند.

کوکی «یادآوری من» (Remember Me)

کوکی نشست (session cookie) فقط برای یک بازه زمانی قابل تنظیم معتبر است (که در تنظیمات کلی → تب سیستم → طول عمر نشست تنظیم می‌شود). اگر کوکی نشست منقضی شده باشد، اطلاعات نشست به‌گونه‌ای نخواهد بود که کاربر وارد شده محسوب شود. معمولاً طول عمر نشست کوتاه در نظر گرفته می‌شود تا احتمال استفاده دیگران از دستگاه مشترک و دسترسی غیرمجاز به سایت کاهش یابد.

اگر کاربر تنها استفاده‌کننده دستگاه باشد، هنگام ورود می‌تواند گزینه «یادآوری من» (Remember Me) را فعال کند. در این حالت جوملا یک کوکی «یادآوری من» (که مدت زمان زندگی طولانی‌تری نسبت به کوکی نشست دارد) صادر می‌کند. اگر بعداً کاربر دوباره به سایت جوملا مراجعه کند و کوکی نشست منقضی شده باشد، این کوکی «یادآوری من» باعث می‌شود که کاربر به عنوان وارد شده تلقی شود.

نحوه پیاده‌سازی کوکی «یادآوری من» در جوملا

پیاده‌سازی کوکی «یادآوری من» در جوملا توسط یک پلاگین سیستم انجام می‌شود که مسیر آن:

plugins/system/remember

 است و این پلاگین به رویداد `onAfterInitialisation` گوش می‌دهد. وقتی کاربر مهمان (guest) است و وارد نشده، این پلاگین وجود کوکی «یادآوری من» را بررسی می‌کند و اگر آن را بیابد، کاربر مرتبط را بازیابی کرده و او را وارد شده در نظر می‌گیرد (این عملیات در واقع توسط پلاگین «احراز هویت - کوکی» انجام می‌شود).

نکته مهم درباره نام کوکی «یادآوری من»

نام واقعی این کوکی شامل نام دایرکتوری اسکریپت PHP است که کوکی را صادر کرده، و به طور پیش‌فرض این دایرکتوری، همان پوشه اصلی است که فایل `index.php` در آن قرار دارد.

پس اگر می‌خواهید از کوکی «یادآوری من» استفاده کنید، اسکریپت شما باید در همان پوشه سطح بالای سایت (که `index.php` قرار دارد) قرار گرفته باشد.

روش استفاده از کوکی «یادآوری من» در اسکریپت شما

کافی است رویداد `onAfterInitialise` را در اسکریپت خودتان فراخوانی (trigger) کنید تا پلاگین‌های سیستم فراخوانی شوند و عملکرد «یادآوری من» فعال شود. سپس می‌توانید چک کنید وضعیت کاربر در نشست (session) چیست. کد کامل مثال:

 

 
// نمونه‌سازی برنامه کاربردی (Application)
$app = $container->get(\Joomla\CMS\Application\SiteApplication::class);

// تعیین برنامه به عنوان برنامه کلی
\Joomla\CMS\Factory::$application = $app;

// دریافت داده‌های کاربر از نشست
$user = $app->getSession()->get('user');
if ($user->id > 0) {
    echo "<h3>ورود شده به عنوان {$user->name}</h3>";
} else {
    echo "<h3>وارد نشده است</h3>";
}

// بارگذاری پلاگین‌های سیستم و فعال کردن رویداد onAfterInitialise
\Joomla\CMS\Plugin\PluginHelper::importPlugin('system');
$app->triggerEvent('onAfterInitialise');

// پس از فراخوانی رویداد، ممکن است کاربر وارد شده باشد (با کوکی یادآوری من)
if ($user->id == 0) {
    $user = $app->getSession()->get('user');
    if ($user->id > 0) {
       echo "<h3>ورود با کوکی یادآوری من به نام {$user->name}</h3>";
    } else {
        echo "<h3>هنوز وارد نشده است</h3>";
    }
}

نکات مهم:

- اسکریپت PHP سفارشی شما باید در همان پوشه سطح بالایی قرار داشته باشد که فایل اصلی `index.php` سایت جوملا وجود دارد.

- همچنین باید پلاگین‌های «Remember Me» (به یاد سپردن ورود) و «Authentication - Cookie» (احراز هویت با کوکی) فعال باشند تا عملکرد کوکی «یادآوری من» در اسکریپت شما قابل استفاده باشد.

نکته جانبی: 

اگر در حال آزمایش با طول عمر نشست (Session Lifetime) بسیار کوتاه هستید و متوجه نشده‌اید چرا کوکی نشست منقضی نمی‌شود، دلیل آن ممکن است عملکرد «نگهداری زنده» (Keep Alive) در جوملا باشد.

جوملا با کمک کد جاوااسکریپت درخواست‌های HTTP «نگهداری زنده» به سرور ارسال می‌کند تا کوکی نشست را به‌روزرسانی کند و از منقضی شدن آن جلوگیری کند تا نشست کاربری باز بماند.

ورود به جوملا با استفاده از نام‌کاربری و رمز عبور سخت‌کد شده

می‌توانید مانند زیر کاربر را با نام‌کاربری و رمزعبور مشخص وارد کنید: 

 
$username = "myuser";
$password = "mypassword";
$credentials = ['username' => $username, 'password' => $password];
$logged_in = $app->login($credentials, []);

نکات مهم:

- همانطور که در اسکریپت ساده پیشین دیده‌اید، لزوماً لازم نیست وارد شوید تا بتوانید کوئری پایگاه داده انجام دهید. اما اگر از کدهای جوملا استفاده می‌کنید (مثلاً مدل مقاله از کامپوننت `com_content`)، احتمالاً کد مذکور قبل از انجام عملیات، دسترسی‌ها و مجوزها را بررسی می‌کند. پس باید به عنوان یک کاربر مجاز وارد شده باشید تا بتوانید از آن عملکردها استفاده کنید.

- اگر از نام‌کاربری و رمزعبور ثابت در اسکریپت استفاده کنید، هیچ تضمینی وجود ندارد که فردی که اسکریپت را اجرا می‌کند، کاربر معتبری در سایت جوملا باشد. بنابراین یا باید عملکرد اسکریپت برای همه باز باشد یا باید روش دیگری برای محدود کردن دسترسی به اسکریپت تعیین کنید.

خطرات مهم استفاده از نامکاربری و رمزعبور سختکد شده

در این روش ممکن است فردی بعد از اجرای اسکریپت به طور «ناخواسته» با آن نام‌کاربری وارد سایت شما شود.

چرا؟

زیرا فرآیندهای راه‌اندازی جوملا برخی توابع ثبت شده در php برای هنگام پایان اجرای اسکریپت ثبت می‌کنند. این توابع هنگام پایان اسکریپت:

- داده‌های نشست را ذخیره می‌کنند که شامل نام‌کاربری است که به صورت سخت‌کد (hard code) شده وارد شده است

- در پاسخ HTTP به مرورگر، کوکی نشست ارسال می‌شود.

اگر کاربر پس از اجرای اسکریپت به سایت جوملا مراجعه کند، مرورگر کوکی نشست را ارسال کرده، جوملا آن را می‌خواند و داده‌های نشست را بازیابی می‌کند، و کاربر «وارد شده» با اطلاعات موجود در آن را شناسایی می‌کند.

توصیه مهم

پس بهتر است صریحاً قبل از پایان اسکریپت، کاربر را از سیستم خارج (logout) کنید:

 
$app->logout();

ولی باز هم اگر کد شما باعث ایجاد خطا (Exception) شود و آن را در ساختار try/catch مدیریت نکرده باشید، کنترل به مدیریت کلی استثناهای جوملا داده می‌شود و اسکریپت ناگهانی به پایان می‌رسد بدون اینکه logout فراخوانی شود و این خطر همچنان باقی است.

خلاصه و نکات تصمیم‌گیری درباره روش ورود در اسکریپت‌ها

هنگامی که قصد انتخاب روش «ورود» در اسکریپت‌ها را دارید، موارد زیر را در نظر داشته باشید:

- آیا اجرای اسکریپت برای همه کاربران آزاد است یا خیر؟

- آیا سایر روش‌ها دسترسی به اسکریپت را محدود کرده‌اند؟

- آیا برای عملکرد صحیح کد جوملا در اسکریپت، حتماً لازم است که کاربر وارد شده (logged-in) باشد؟

- اگر اطلاعات ورود را سخت‌کد می‌کنید، چه مکانیزم‌های مطمئنی دارید که از ورود ناخواسته کاربران ناشناس جلوگیری کند؟

نصب اسکریپت سفارشی

اگر به سیستم فایل وب‌سایت‌تان دسترسی دارید، به راحتی می‌توانید اسکریپت خود را در پوشه مورد نظر کپی کنید.

اما اگر می‌خواهید اسکریپت‌تان را به روش استاندارد نصب کنید (مثلاً با ایجاد یک افزونه یا پکیج نصب‌شدنی جوملا)، باید در فایل مانیفست (manifest file) نوع اکستنشن (extension type) را برابر `file` قرار دهید.

نمونه فایل manifest برای اسکریپت نوع فایل (`file`):

 
<?xml version="1.0" encoding="utf-8"?>
<extension type="file" version="5.1" method="upgrade">
    <name>Custom Script</name>
    <version>1.0</version>
    <description>Script to count the number of articles in the database</description>
    <fileset>
        <files target="cli">
            <filename>myscript.php</filename>
        </files>
    </fileset>
</extension>
 

توضیحات:

- نوع اکستنشن `file` به جوملا می‌گوید که فقط یک یا چند فایل خاص نصب شود.

- در بخش `<fileset>` مشخص شده که فایل `myscript.php` باید در پوشه `cli` قرار بگیرد.

- بنابراین با نصب این بسته، اسکریپت شما به پوشه `cli` جوملا منتقل می‌شود.

در نتیجه، با استفاده از این روش می‌توانید اسکریپت‌های خود را به صورت بسته قابل نصب در جوملا ارائه دهید و مدیریت آن‌ها آسان‌تر خواهد بود.