ورودی جوملا
- محمد علایی
- منتشر شده در
- زمان خواندن 4 دقیقه
مقدمه
قابلیت ورودی در جوملا یک رابط کاربری ساده برای دریافت و پاکسازی (sanitize) دادههای بسیاری از سوپرگلوبالهای PHP فراهم میکند، از جمله:
- پارامترهای GET, POST,HTTP
- پارامترهای مسیریابی ("option"، "view"، "layout" و غیره) که به دنبال تجزیه URL به صورت SEF (دوستدار سئو) تولید میشوند
- اطلاعات مربوط به فایلهای بارگذاری شده
- دادههای PHP $_SERVER – اطلاعات مربوط به سرور و محیط اجرای برنامه
برای شروع، باید یک نمونه از کلاس ورودی جوملا را ایجاد کنید:
use Joomla\CMS\Factory;
$input = Factory::getApplication()->getInput();
اگر کد شما درون یک کنترلر است که از `Joomla\CMS\MVC\Controller\BaseController` ارث میبرد، این کار قبلاً انجام شده و متغیری محافظتشده به نام `$input` در دسترس است، پس میتوانید به جای استفاده از `$input`، از `$this->input` استفاده کنید بدون نیاز به مقداردهی اولیه متغیر.
این سند توضیح میدهد چگونه میتوانید دادههای ذکر شده را دریافت کنید.
دریافت پارامترهای جداگانه با فیلترها
برای دریافت مقدار یک پارامتر خاص از کد زیر استفاده کنید:
$val = $input->get(param_name, default_value, filter);
که در آن:
- `param_name` رشتهای است که نام پارامتری را که میخواهید دریافت کنید مشخص میکند
- `default_value` مقداری است که اگر پارامتر یافت نشد بازگردانده میشود (ممکن است رشته، عدد صحیح، آرایه، null و غیره باشد)
- `filter` رشتهای است که یکی از فیلترهای زیر را مشخص میکند. در صورت عدم تعیین آن، فیلتر پیشفرض "cmd" استفاده میشود.
میتوانید این روش را برای دریافت هر نوع پارامتر GET، POST یا مسیریابی استفاده کنید. روشهای دریافت پارامترهای GET و POST به صورت اختصاصی در ادامه مستند آمده است.
فیلترهای موجود
در زیر لیستی از فیلترهای موجود به همراه توضیح کوتاه آمده است. فیلترها میتوانند به صورت بزرگ یا کوچک نوشته شوند. اگر دو فیلتر در یک خط آمدهاند، معادل هم هستند. برای جزئیات بیشتر میتوانید کد منبع زیر را بررسی کنید.
'libraries/vendor/joomla/filter/src/InputFilter.php'
- INT, INTEGER: اولین عدد صحیح موجود در مقدار پارامتر را برمیگرداند. مثلاً اگر پارامتر `"abc123def456"` باشد، مقدار برگشتی 123 به صورت عدد صحیح خواهد بود.
- UINT: عدد صحیح بدون علامت برمیگرداند. مثلاً `?p1=-2` خروجی 2 میشود (علامت منفی حذف میشود).
- FLOAT, DOUBLE: اولین عدد اعشاری موجود را برمیگرداند.
- BOOL, BOOLEAN: دقت کنید! اگر مقدار `?p1=false` باشد، مقدار "false" رشتهای است غیرخالی و به عنوان true در نظر گرفته میشود.
- WORD: تمام کاراکترهایی که حروف یا زیرخط نیستند را حذف میکند.
- ALNUM: تمام کاراکترهایی که حروف یا اعداد نیستند را حذف میکند.
- CMD (پیشفرض): تمام کاراکترهایی که حروف، اعداد، زیرخط، نقطه یا خط تیره نیستند را حذف کرده و نقطههای ابتدایی را نیز پاک میکند. این فیلتر اغلب برای دریافت پارامترهایی مانند `?option=controller.task` استفاده میشود.
- BASE64:کاراکترهایی غیر از حروف، اعداد، `/`، `+` یا `=` را حذف میکند. این فیلتر برای دریافت URLهای رمزگذاری شده به صورت base64 کاربرد دارد (مثلاً برای بازگشت به URL اصلی پس از ورود موفق). دقت کنید که این فیلتر تنها فیلتر است و رمزگشایی base64 را انجام نمیدهد.
- STRING: کاراکترهای HTML entities را به شکل صحیح تبدیل میکند (مثلاً `<` به `<`)، سپس تمام تگهای HTML را حذف میکند.
- HTML: فقط تگهای HTML را حذف میکند بدون تبدیل HTML entities قبلی. مثال:
"<br><test>filter" will return "<test>filter"
- ARRAY: فیلترینگ انجام نمیدهد، فقط تلاش میکند مقدار ورودی را به آرایه تبدیل کند.
- PATH: ورودی را به رشته تبدیل و به عنوان مسیر فایل اعتبارسنجی میکند. مسیرهای لینوکس و ویندوز را بررسی میکند ولی مسیرهای مطلق یا مسیرهای با اسلش انتهایی را قبول نمیکند. اگر نامعتبر بود مقدار رشته خالی باز میگرداند.
- RAW: هیچ فیلترینگی انجام نمیدهد! مراقب باشید که میتواند منجر به حملات تزریق شود.
- USERNAME: کاراکترهایی که در نام کاربری جوملا مجاز نیستند را حذف میکند.
همچنین میتوانید به جای تعیین فیلتر به روش بالا، از متدهای اختصاصی ورودی بهره ببرید، مثلاً:
// به جای:
$input->get('name', '', 'STRING');
// میتوانید استفاده کنید:
$input->getString('name', '');
// به جای:
$input->get('memberId', 0, 'INT');
// میتوانید استفاده کنید:
$input->getInt('memberId', 0);
البته روش `getArray()` متفاوت است که در ادامه مستندات جوملا اشاره شده است.
اگر بخواهید یک شیء (object) دریافت کنید، میتوانید به شکل زیر عمل کنید:
$obj = $input->get(param_name, null, null);
آرایهها
آرایهها در دو حالت کاربرد دارند:
- وقتی دادهها به صورت آرایه از کلاینت ارسال میشوند، مانند زمانی که از فرمهای جوملا استفاده میکنید و دادههای فرم به صورت آرایه (به طور پیشفرض با نام jform[]) به سرور ارسال میشود.
- وقتی دادهها به صورت پارامترهای جداگانه ارسال میشوند اما میخواهید آنها را به صورت آرایه دریافت کنید.
ابتدا حالت دوم را بررسی میکنیم.
- خواندن پارامترهای منفرد در قالب آرایه
چند روش برای این کار وجود دارد که تفاوتشان در نوع فیلتر اعمال شده است.
گزینه ۱
$value = $input->get('p1', array(), "array");
همانطور که اشاره شد، مقدار برگردانده شده آرایه است و مقدار `value[0] ` برابر با مقدار رشتهای `p1` خواهد بود. هیچ فیلترینگی روی `p1` انجام نمیشود.
گزینه ۲
$values = $input->getArray();
این روش تمام پارامترها را به صورت یک آرایه انجمنی (associative array) بازمیگرداند که نام پارامترها را به مقدارشان نگاشت میکند. فیلتر پیشفرض `"STRING"` روی همه پارامترها اعمال میشود. مقدار هر پارامتر یا یک رشته است (برای پارامترهای منفرد) یا یک آرایه انجمنی (اگر خودش آرایه باشد که باز هم فیلتر `"STRING"` روی هر جزء آن اعمال میشود).
توجه: فیلتر پیشفرض برای متد `get()` برابر با `"CMD"` است اما برای `getArray()`، پیشفرض `"STRING"` است.
گزینه ۳
میتوانید مشخص کنید که کدام پارامترها را میخواهید به صورت آرایه دریافت کنید:
$values = $input->getArray(array('p1' => '', 'p2' => '', 'p3' => ''));
این یک آرایه انجمنی با کلیدهای 'p1', 'p2' و 'p3' را بازمیگرداند که هر کدام به مقادیرشان اشاره می کنند. فیلتر `"STRING"` روی همه اعمال میشود.
در واقع همانند گزینه ۲ است اما فقط پارامترهای مشخصشده را برمیگرداند.
گزینه ۴
علاوه بر مشخص کردن پارامترها، میتوانید فیلتر دلخواه را برای هرکدام تعیین کنید:
$values = $input->getArray(array('p1' => 'string', 'p2' => 'int', 'p3' => 'cmd'));
در این حالت `$values` آرایهای انجمنی است که هر پارامتر مطابق فیلتر مشخصشده پالایش و نوع متناسب با فیلتر انتخاب میشود (مثلاً مقدار `p2` یک عدد صحیح است).
شما همچنین میتوانید آرایههای تو در تو بسازید برای مقادیر پیچیدهتر:
$values = $input->getArray(array(
'jform' => array(
'title' => 'string',
'quantity' => 'int',
'state' => 'int'
)
));
خواندن پارامترهای آرایهای
اگر پارامترهایی که میخوانید از قبل به شکل آرایهای هستند، چند روش برای خواندن آنها به عنوان آرایه PHP وجود دارد که برخی مشابه بالا هستند. فیلتر اعمال شده میتواند متفاوت باشد. فرض کنید آرایهای به نام `jform` به سرور ارسال شده است.
گزینه ۱
$value = $input->get('jform');
مقدار `$value` یک آرایه انجمنی خواهد بود که کلیدهای آن مثل کلیدهای آرایه `jform` است. فیلتر `"CMD"` روی همه مقادیر آن اعمال میشود و همه مقادیر از نوع رشته خواهند بود.
گزینه ۲
میتوانید تعیین کنید کدام فیلتر روی مقادیر اعمال شود، مثلاً:
$value = $input->get('jform', array(), "STRING");
در این حالت باز هم `$value` آرایه انجمنی است ولی فیلتر `"STRING"` به جای `"CMD"` روی هر عضو آرایه اعمال میشود.
گزینه ۳
میتوانید مشخص کنید کدام زیرمقادیر آرایه `jform` را میخواهید و فیلتر مربوط به هر کدام را تعیین کنید:
$values = $input->getArray(array(
'jform' => array(
'title' => 'string',
'quantity' => 'int',
'state' => 'int'
)
));
این همان روش گزینه ۴ در بخش قبل است.
پارامترهای GET و POST
خواص `get` و `post` در کلاس ورودی (Input) جوملا به شما امکان دسترسی به پارامترهای GET, POST و HTTP را میدهند:
- `$input->get` : دسترسی به پارامترهای GET
- `$input->post` : دسترسی به پارامترهای POST
میتوانید متدهای `get()` و `getArray()` را روی این خواص فراخوانی کنید. همانطور که پیشتر گفته شد، فیلتر پیشفرض برای `get()` برابر "CMD" و برای `getArray()` برابر "STRING" است.
در صورتی که سایت شما از URLهای SEF استفاده کند، توجه کنید که این URLها پارامترهای مسیریابی مانند "option"، "view" و غیره را که توسط روتری جوملا تولید شدهاند شامل نمیشوند. فقط پارامترهای واقعی GET و POST بازگردانده میشوند.
نمونههای کاربردی:
- دریافت پارامتر GET با نام `'p1'`:
$value = $input->get->get('p1');
که مقدار آن با فیلتر `"CMD"` پالایش میشود.
- دریافت پارامتر GET `'p1'` با فیلتر `"INT"`:
$value = $input->get->get('p1', 0, "int");
- دریافت همه پارامترهای GET:
$value = $input->get->getArray();
که خروجی آرایهای انجمنی از پارامترهای GET است که روی مقادیر آن فیلتر `"STRING"` اعمال شده است.
- دریافت یک پارامتر خاص مانند 'p1' در POST:
$value = $input->post->get('p1');
مقدار با فیلتر `"CMD"` پالایش میشود.
- دریافت همه پارامترهای POST:
$value = $input->post->getArray();
که خروجی آرایهای انجمنی از پارامترهای POST است که روی مقادیر آن فیلتر `"STRING"` اعمال شده است.
- برای دریافت پارامترهای POST آرایهای مثل `jform` بدون اعمال هیچ فیلتر:
$value = $input->post->get("jform", array(), "array");
این روش در هسته MVC جوملا برای دریافت اطلاعات خام فرم استفاده میشود. دادههای خام به تابع `validate` مدل فرستاده میشوند که هم فیلترینگ و هم اعتبارسنجی دادهها را انجام میدهد. فیلترینگ در آنجا با فیلترهای فرم که با تابع `bind()` توصیف شدهاند انجام میشود (این فیلترها متفاوت از فیلترهای ورودی این بخش هستند). سپس اعتبارسنجی سمت سرور انجام میشود.
فایلها
ممکن است شما در فرم جوملای خود یک عنصر ورودی HTML با نوع `type="file"` داشته باشید تا کاربر بتواند فایلی را برای وبسایت شما آپلود کند. در این حالت، PHP محتوای درخواست POST را در یک فایل در دایرکتوری موقتی (tmp) خود ذخیره میکند و اطلاعات مربوط به آپلود را در دسترس میگذارد. جوملا یک رابط کاربری ساده برای دسترسی به این اطلاعات فراهم کرده است. (این اطلاعات از طریق پارامترهای GET/POST در دسترس نیست.)
فرض کنید فرمی مشابه نمونه زیر دارید:
<form action="<?php echo Route::_('index.php?option=com_example&task=file.submit'); ?>" enctype="multipart/form-data" method="post">
<input type="file" name="jform1[test][]" />
<input type="file" name="jform1[test][]" />
<input type="submit" value="submit" />
</form>
برای خواندن فایلهای ارسال شده، از این دستور استفاده کنید:
$files = $input->files->get('jform1');
متغیر `$files` چیزی شبیه به ساختار زیر خواهد بود:
Array
(
[test] => Array
(
[0] => Array
(
[name] => youtube_icon.png // نام فایل در سیستم کاربر
[type] => image/png // نوع فایل (mime type)
[tmp_name] => /tmp/phpXoIpSD // نام فایل موقت روی سرور
[error] => 0 // صفر به معنای بدون خطاست
[size] => 34409 // اندازه فایل به بایت
)
[1] => Array
(
[name] => Younger_Son_2.jpg
[type] => image/jpeg
[tmp_name] => /tmp/phpWDE7ye
[error] => 0
[size] => 99529
)
)
)
JSON
اگر دادهها را به صورت رشته JSON در دادههای POST یک درخواست Ajax ارسال کنید، میتوانید این دادهها را به صورت یک آرایه انجمنی (associative array) با استفاده از:
$jsonArray = $input->json->get(param_name);
دریافت کنید.
توجه داشته باشید این روش زمانی کاربرد دارد که کل بدنه درخواست POST یک رشته JSON باشد و نه اینکه چند پارامتر POST وجود داشته باشد که یکی از آنها JSON باشد. جوملا دادهها را از `php://input` دریافت کرده و تابع `json_decode()` را روی آن فراخوانی میکند.
همچنین اگر از این روش استفاده میکنید، توصیه میشود توکن امنیتی (security token) را به عنوان یکی از پارامترهای GET در URL ارسال کنید.
نمونه کاری
برای نمونهای از نحوه استفاده، میتوانید مشاهده کنید که جوملا چگونه مقدارهای دسترسی (Permissions) را در تنظیمات کلی (Global Configuration) بهروزرسانی میکند:
- در فایل جاوااسکریپت زیر به تابع `sendPermissions()` مراجعه کنید
'media/system/fields/joomla-field-permissions.js'
- در فایل PHP زیر به تابع `storePermissions()` زیر مراجعه کنید
`administrator/components/com_config/src/Model/ApplicationModel.php'
دادههای سرور
میتوانید دادههای `$_SERVER` در PHP را به همراه فیلتر مناسب به این صورت دریافت کنید:
$val = $input->server->get(param_name, default_value, filter);
تنظیم مقادیر ورودی
توابع `set()` و `def()` به شما اجازه میدهند پارامترهای ورودی و مقدارشان را تعیین کنید:
$input->set('p2', "someval");
مقدار پارامتر `p2` را به رشته `"someval"` تنظیم میکند (اگر پارامتر موجود نباشد ایجادش میکند).
$input->def('p2', "someval");
پارامتر `'p2'` را ایجاد و مقدارش را روی `"someval"` تنظیم میکند، اما فقط در صورتی که `'p2'` قبلاً وجود نداشته باشد. اگر `'p2'` قبلاً تعریف شده باشد، این متد هیچ تغيیری نمیدهد.
کد نمونه ماژول
در ادامه کدی برای یک ماژول ساده جوملا آمده است که میتوانید آن را نصب و اجرا کنید تا نحوه دریافت مقادیر پارامترها را مشاهده کنید.
در یک پوشه به نام mod_input دو فایل زیر را ایجاد کنید:
فایل mod_input.xml :
<?xml version="1.0" encoding="utf-8"?>
<extension type="module" version="3.1" client="site" method="upgrade">
<name>Input demo</name>
<version>1.0.1</version>
<description>کدی برای نمایش نحوه استفاده از کلاس Input جوملا برای دریافت پارامترهای HTTP</description>
<files>
<filename module="mod_input">mod_input.php</filename>
</files>
</extension>
فایل mod_input.php :
<?php
defined('_JEXEC') or die('دسترسی مجاز نمیباشد');
use Joomla\CMS\Factory;
$app = Factory::getApplication(); // معادل $app = JFactory::getApplication();
$input = $app->getInput();
if ($input->exists('p1'))
{
$v1 = $input->get('p1', 0, "INT"); // معادل $input->getInt('p1', 0)
echo "<p>مقدار صحیح (INT) پارامتر p1 برابر است با: $v1</p>";
$v1 = $input->get('p1', 0, "UINT"); // عدد صحیح بدون علامت
echo "<p>مقدار عدد صحیح بدون علامت (UINT) پارامتر p1 برابر است با: $v1</p>";
$v1 = $input->get('p1', 0, "string");
echo "<p>مقدار رشتهای پارامتر p1 برابر است با: $v1</p>";
}
else
{
echo "<p>پارامتر p1 مشخص نشده است</p>";
}
پوشه `mod_input` را فشرده (zip) کنید تا فایل `mod_input.zip` ساخته شود.
در مدیریت جوملا به بخش نصب افزونهها (Install Extensions) بروید و از طریق زبانه «بارگذاری بسته» (Upload Package File) همین فایل زیپ را انتخاب و نصب کنید.
برای نمایش این ماژول، آن را در صفحه ماژولها پیدا کرده و ویرایش کنید، سپس:
- وضعیت آن را روی منتشر شده (Published) تنظیم کنید
- موقعیتی روی صفحه انتخاب کنید تا ماژول در آن نمایش داده شود
- در زبانه تخصیص منو (Menu Assignment) صفحات مورد نظر برای نمایش ماژول را مشخص کنید
یک صفحه وب که این ماژول در آن قرار دارد را نمایش دهید. سپس پارامتر `p1` را به URL اضافه کنید:
- اگر URL هیچ پارامتری ندارد، به صورت `?p1=123abc` اضافه کنید
- اگر URL پارامترهای دیگری دارد، به صورت `?p1=123abc` اضافه کنید
شما نتایج دریافت پارامتر `p1` را خواهید دید که با فیلترهای مختلف پردازش شده است. میتوانید با مقدارهای مختلف برای `p1` و فیلترهای متفاوت آزمایش کنید و حتی با ابزاری مثل curl پارامترهای POST ارسال کنید تا کارکرد دریافت پارامترهای POST را نیز ببینید.
کد نمونه کامپوننت
شما میتوانید این کد نمونه کامپوننت را دانلود کرده و به عنوان پایهای برای آزمایش دریافت پارامترهای POST، فایلها و موارد دیگر استفاده کنید. در فایل `src/Controller/PostController.php` از کلاس `Input` برای دریافت پارامترهای POST و ذخیرهی آنها در دادههای نشست (session) استفاده شده است:
// نام آرایه 'jform' باید با مقدار 'control' => 'jform' در کد مدل هماهنگ باشد
$data = $this->input->post->get('jform', array(), 'array');
// ذخیره دادهها در سشن برای اینکه بتوانیم در DisplayController آنها را نمایش دهیم
$app->setUserState('com_sample_form_field.post', $data);
پس از انجام ریدایرکت و برگشت به صفحهی نمایش فرم، ویو (View) این دادههای ذخیره شده را در فایل `src/View/Sample/HtmlView.php` میخواند:
// بررسی وجود دادههای POST ارسالی در فرم قبلی
$this->postdata = Factory::getApplication()->getUserState('com_sample_form_field.post', null);
// پاکسازی دادهها برای آماده شدن جهت ارسال بعدی
Factory::getApplication()->setUserState('com_sample_form_field.post', array());
سپس این دادهها در فایل قالب `tmpl/sample/default.php` به شکل زیر نمایش داده میشوند:
<?php
ob_start();
var_dump($this->postdata);
$post = ob_get_contents();
ob_end_clean();
?>
<?php echo '<pre>' . htmlspecialchars($post, ENT_QUOTES) . '</pre>'; ?>
با پیروی از این الگو، میتوانید در `PostController` روشها و فیلترهای مختلفی را امتحان کرده، نتیجه را در سشن ذخیره کنید و پس از ریدایرکت، دادهها را در فایل ویو خوانده و در قالب نمایش دهید.