Емуляція директиви register_globals on
Є в PHP така цікава директива, що має назву register_globals, визначена в php.ini. Директива вказує компілятору, що значення вхідних (глобальних) змінних слід вилучити з системних масивів і у вигляді самостійних змінних. До таких даних відноситься все, що передається в скрипт "зовні": дані форм, дані з URL, cookie і так далі. Особисто мені ця директива подобається, бо вона заощаджує час написання скриптів і робить їх більш читаними. Порівняйте самі, що виглядає приємніше та зручніше для ока:
Взагалі, особисто я люблю PHP в основному за його легкість читання і швидке написання.
Тим не менш, у кожній зручній фішці обов'язково приховується якась гидота. Не обійшлося без проблем і з реєстрацією глобальних змінних. Неуважні програмісти залишали у своїх скриптах дірки, які можна було виявити та використати з боку користувача. Простіше кажучи, якщо змінна всередині скрипта брала участь у роботі, але не встановлювалася цим же скриптом, її можна було встановити "зовні", передавши її ім'я та значення скрипт через URL, cookie або ще як-небудь.
Ця дірка, яку записали в розряд "дір у PHP" особисто я вважаю діркою у програмуванні, а не в мові, змусила розробників PHP рекомендувати відключення register_globals. Що відразу було сприйнято громадськість як керівництво до дії: всі почали вимикати реєстрацію глобальних змінних своїх серверах.
Можливо, це й не так уже й погано, судити не буду, адже заради безпеки іноді зводять і городять набагато більше, ніж код самого завдання.
Але повальна відмова від реєстрації глобальних змінних призвела до нової проблеми - багато скриптів треба переписувати мало не з нуля, бо треба перевірити кожен рядок, знайти всевикористовувані змінні та поміняти їх на "захищені" аналоги.
Наскільки я розумію, подібне завдання, зрештою, зводиться до того, щоб на початку роботи скрипту привласнити звичним змінним їх значення з глобальних масивів $_REQUEST, $_POST, $_GET, $_COOKIE, $_SERVER і т.д.
Тож до чого ж я веду. А ось до чого. Нормальний програміст не повинен допускати дірок у своїх скриптах, подібних до дірок на основі автоматичної реєстрації глобальних змінних, але хіба це буде хвилювати адміністратора сервера, на якому вирішено розмістити код? Ні, це його не хвилює і він по-своєму має рацію.
Тому якщо ви написали на замовлення програму з використанням глобальних змінних, а замовник вирішив встановити її на сервері, де заборонена їх реєстрація, то нічого працювати не буде - у вас проблеми.
Якщо ви вирішили перенести всі свої проекти на новий хостинг, а провайдер забороняє використовувати реєстрацію глобальних змінних – у вас є проблеми.
Як бути? Не вираховувати десятки, сотні, а іноді й тисячі сторінок коду в пошуках використання глобальних змінних.
Я пропоную наступне рішення. Потрібно просто емулювати роботу register_globals в одному окремо взятому скрипті або на початку об'єктно-орієнтованої структури.
Як це зробити. Та не надто вже й складно. Давайте міркувати логічно: імена змінних та їх значення містяться у відповідних глобальних масивах. Як правило, використовується масив $_REQUEST, який поєднує в собі всі змінні GET, POST і COOKIE. Тобто. все, що передається скрипту із браузера, те, з чим працюють скрипти.
Отже, треба витягти з масиву імена змінних, значення змінних і присвоїти першому - друге. Витягти - не проблема, для цього підійде функція перебору всіх осередків масивуforeach(), але як присвоїти? Якщо у нас у$_REQUEST[username]міститься "atos", то як програмно створити змінну$usernameзі значенням "atos"? Ми не можемо заздалегідь знати, які імена змінних будуть у масиві $_REQUEST.
Ось тут нам допоможе дивовижна функція eval(), яка рідко використовується у звичайному програмуванні, але буквально незамінна у деяких випадках. Про саму функцію варто написати окремо, тому скажу лише пару слів для тих, хто з нею не знайомий.
Функціяeval()змушує PHP розглядати звичайний текст, що міститься в змінній, як фрагмент PHP-коду. Говорячи мовою прикладів, результати роботи рядків
Ось eval() і допоможе нам оголосити всі змінні із масиву $_REQUEST. Виглядає це дуже коротко:
Вставте цей цикл на початку вашого скрипта; він перебере масив глобальних змінних і оголосить їх не гірше за register_globals. Можливо навіть і краще, т.к. Світових масивів багато, а витягувати змінні не обов'язково з усіх. Як правило, даних із масиву $_REQUEST - цілком достатньо.
Однак не варто забувати і про безпеку вашого коду. Зверніть увагу на специфіку роботи функції eval() – вона обробить весь код, переданий їй як параметр. Будьте обережні, вживіть заходів безпеки, щоб зловмисник не підсунув як назву або вміст змінної шматок свого php-коду або просто невірні дані, здатні викликати помилку (наприклад, ім'я змінної, що починає з цифри або іншого невирішеного символу).
Як бачите, теж простий метод, заснований на тому, що значення змінної $k використовується як ім'я нової змінної. Зручно. Відрізняється автоматичною реєстрацією як змінних, а й масивів. У випадку зметодом eval() доведеться перевіряти кожну змінну на is_array() та розгортати (реєструвати) її додатково, якщо такий масив вам потрібен.
І найпростіший метод – extract().
Весь код нашого прикладу виглядатиме так:
Це "тупий" метод, що "розвертає" в змінні все, що міститься в масиві $_REQUEST. Немає абсолютно ніякої гнучкості в цьому підході - вплинути на реєстрацію змінних або ввести будь-який контроль ви не зможете, але дуже лаконічно.
І, нарешті, не забувайте про те, що register_globals можна активувати не тільки у конфігураційному файлі Apache, але й у файлі .htaccess вашого сайту.