Archive for category PHP
PHP-кодеру на пам’ять. Випуск 2
Можливі проблеми з UTF-8
Якщо в проекті використовуються файли з кодировкою Юнікод, то можлива наступна проблема:
Warning: Cannot modify header information – headers already sent by (output started at…
Якщо все перевірено, ніде немає зайвих пробілів і т.д., а проблема збереглась, то це може означати, що файл був збережений не в “чистому” UTF-8, а в UTF-8 з сигнатурою(BOM). Деякі текстові редактори уміють зберігати файли в обох варіантах – наприклад Scite або Notepad2. Потрібно просто відкрити і зберегти файл в потрібній кодировці.
Через UTF-8 з сигнатурою(BOM) можлива також поява порожнього рядку “нізвідки” при виводі HTML через PHP.
foreach – деякі особливості
Щоб вивести певні результати за допомогою цього циклу можливі кілька варіантів. А от щоб ЗМІНИТИ САМ МАСИВ допустимий лише один шлях:
Працює:
foreach($rows as $k=>$v){
$rows[$k]['news_date'] = date('r', strtotime($rows[$k]['news_date']));
}
Не працює (оскільки ми змінюємо змінну зі значенням, а не сам масив):
foreach($rows as $k=>$v){
$v['news_date'] = date('r', strtotime($v['news_date']));
}
Змінні в PHP
Наштовхнувся в одному блозі на цікавий пост присвячений двом змінним в РНР:
DIRECTORY_SEPARATOR і PATH_SEPARATOR.
DIRECTORY_SEPARATOR – як випливає з назви, зберігає розділювач між папками, який різниться в залежності від ОС (’/'- Unix, ‘\’ – Windows).
Незважаючи на те, що Windows зрозуміє вас навіть, якщо розділяти папки символом ‘/’, всеодно коректніше використовувати DIRECTORY_SEPARATOR
PATH_SEPARATOR - зберігає розділювач для параметру РНР include_path (всі шляхи, по яким ваші скрипти шукають бібліотеки для підключкення)
Так ось, під Windows цей розділювач виглядає як ‘;’ під Unix – ‘:’
Коли потрібно підключити якусь бібліотеку (то й же Зенд) і працювати зі скриптом під різними ОС, виникають складнощі.
Донедавна я вирішував ці складнощі “геніальним” скриптиком:
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
ini_set('include_path', ini_get('include_path') . ';' . getcwd() . DS . '..' . DS. 'lib' .';');//WIN
} else {
ini_set('include_path', ini_get('include_path') . ':' . getcwd() . DS . '..' . DS. 'lib' . ':');//another OS
}
Зрозуміло, що в “цивілізованому світі” (а тепер і я) роблять по-іншому:
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR. getcwd() . DS . '..' . DS. 'lib' . PATH_SEPARATOR);
Правильний стиль написання коду від Zend
For files that contain only PHP code, the closing tag (”?>”) is never permitted. It is not required by PHP, and omitting it´ prevents the accidental injection of trailing white space into the response
Ось як воно… А взагалі, почитати рекомендації від Zend ніколи зайвим не буде.
Економія пам’яті в PHP
При операціях з картинками на сервері варто слідкувати за витратами пам’яті на сервері і не забувати про функцію imagedestroy. Наприклад:
$src = imagecreatefromjpeg($file_temp); .... якийсь код imagedestroy($src);
До речі, для тестування і моніторингу скільки саме пам’яті використовує РНР на певний момент, застосовується функція memory_get_usage() (без параметрів)
Сесії в PHP та куки
Тема ніби не з найскладніших, проте в свій час я виявив в ній багато “нового та невідомого”. В результаті з’явилась ця стаття, в котрій я спробував базово окреслити основні особливості функціонування та використання вищезгаданих технологій.
Специфіка протоколу HTTP така, що він не підтримую постійного з’єднання. Тобто “кожний раз – як вперше”. Щоб якось ідентифікувати користувача використовуються сесії та куки.
Вперше звертаючись до сервера, браузер отримує відповідь, в якій будуть куки. Після цього всі свої звернення до сервера браузер буде супроводжувати “прикріпленням” отриманих кук.
По стандарту вони мають структуру:
Set-Cookie: RMID=732423sdfs73242; expires=Fri, 31-Dec-2010 23:59:59 GMT; path=/; domain=.example.net
Кука має ім’я RMID і значення «732423sdfs73242». Термін її зберігання сплине 31 грудня 2010 року в 23:59:59. Шлях «/» і домен «example.net» показують , що можна відкривати куку при перегляді будь-якої сторінки в домені example.net
Браузери дозволяють(на даний момент) зберігати від 30 до 50 кук з одного домена. Firefox 3 наразі зберігає куки в файлі формату SQLite (на них можна подивитись через, скажімо, Firecookie ), ІЕ 7 куки зберігає в звичайних текстових файлах.
Можливі ризики пов’язані з cookies:
1) Завдяки “стороннім” кукам, які можуть розміщуватись рекламними банерами, компанії-господарі цих банерів можуть відслідковувати куди переміщався користувач на сайтах де ці банери встановлені(вихід – можна заборонити браузеру приймати сторонні куки).
Куки може зчитати лише той сайт, якому це ДОЗВОЛЕНО в параметрах конкретно взятого кука (якщо, звичайно, в самому браузері немає “дірок”)
2)Перехоплення трафіка призведе до того, що інформація, яка міститься в куках потрапить до сторонніх людей (вихід – застосовувати https)
3)Міжсайтовий скриптинг – куки передаються на домени, на які передаватись не мали б за допомогою JavaScript і т.д.(вихід – тут уже все залежить від надійності браузера та інших факторів, наприклад – чи ввімкнута в настройках РНР опція session.cookie_httponly)
Використання в РНР:
bool setcookie ( string $name [, string $value [, int $expire [, string $path [, string $domain [, bool $secure [, bool $httponly ]]]]]] )
Приклад:
setcookie(”name”, $value, time() + 600,”/web/index.php”, “.server.com”); //куки на 10 хвилин, доступні тільки з сторінки /web/index.php, яка знаходиться на одному з доменів server.com
$_COOKIE['user_login'] – прочитати куку
setcookie(”name”) – видалити куку
Сесії – це технологія, коли дані про користувача зберігаються на сервері. Де саме? Це залежить від параметрів (в РНР – це session.save_path)
Найчастіше – це звичайні текстові файли(сесії можуть зберігати ще й в БД) з іменем sess_281a977c051ca8c5e24b30f2a492eec2.txt Тут 281a977c051ca8c5e24b30f2a492eec2 – це ідентифікатор сесії.
Але головна проблема – як зв’язати браузер і сервер? Зараз існують 2 шляхи:
1) Через куки – так робиться найчастіше. Але якщо куки відключенні і параметри дозволяють, то використовується спосіб 2:
2)Через сторінку, яка повертається браузеру, сервер сам прописує ідентифікатор сесії в лінки сторінки, в результаті отримуються ссилки виду
site.com.ua/files/Grupa.html?PHPSESSID=6b07180cc1935abde10e4cf0527db4b1
Цей спосіб є не таким безпечним і значно менш популярним.
Вибір між цими двома способами в PHP здійснюється через маніпуляцію параметрами session.use_cookies,session.use_only_cookies, session.use_trans_sid
Час життя сесії як правило 24 хвилини(цей параметр по замовчуванню, його, звичайно, можна змінювати) з моменту останньої активності користувача.
Використання:
session_start(); $_SESSION['username'] = “username”;
Де зберігати дані – тільки в куках? Тільки в сесії? Найпоширенішою практикою зараз є використання сесії для зберігання даних та куків для збереження ідентифікатора сесії. Відповідно, якщо у користувачів відключені куки – то не працює і сесія.
P.S. В процесі розбору з цими технологіями сильно допомогли два плагіни для Firefox: Firecookie(по суті – це плагін для Firebug) і Live HTTP headers
Підрахунок користувачів на сайті через сесію
Цікава функція виловлена на просторах тенет. Підрахунок користувачів онлайн базується на підрахунку сесійних файлів створених для них (звичайно, це спрацює тільки у випадку, якщо ви зберігаєте дані сесії на сервері, а не, скажімо, в базі даних ).
<?php
session_start();
function getUsersOnline() {
$count = 0;
$handle = opendir(session_save_path());
if ($handle == false) return -1;
while (($file = readdir($handle)) != false) {
if (ereg("^sess", $file)) $count++;
}
closedir($handle);
return $count;
}
?>
PHP-кодеру на пам’ять
Дебагінг без зайвих труднощів
Для дебагінгу РНР-програмісти в незалежності від власної кваліфікації часто використовують конструкції echo, var_dump(), print_r і навіть комбінацію echo ‘<pre>’;print_r($obj);echo ‘</pre>’;
Ясно, що в цьому випадку вся потрібна інформація виводиться прямо в браузер. У випадку, якщо потрібні дані будуть містити HTML-теги, браузер їх одразу інтерпретує, і програміст майже нічого не побачить. Наприклад, наступний код не виведе в браузері абсолютно нічого:
<? $tablelist='<input name=csv_dbtable type=hidden value=123>'; echo $tablelist; //Виведе <input name=csv_dbtable type=hidden value=123> і браузер це не покаже ?>
В такому випадку доцільно застосовувати спеціальну РНР функцію – htmlentities. Вона замінює всі теги на спеціальні символи і забезпечує потрібний вигляд інформації, що виводиться
<? $tablelist='<input name=csv_dbtable type=hidden value=123>'; echo htmlentities($tablelist); //Виведе <input name='csv_dbtable' type=hidden value=> через спецсимволи, а браузер це нормально покаже ?>
Генерація картинок в РНР
В PHP можлива робота з графічними файлами через використання бібліотек GD або ImageMagick. Одним із цікавих прикладів такої “співпраці” може бути … генерація картинки з самого php-файлу. Достатньо відправити правильний заголовок браузеру. Наприклад такий (генеруємо png-файл з файлу png_resize.php):
<?
header("Content-type: image/png");
//далі йде код по створенню файла
?>
Потім залишається викликати цей файл:
<img id=text_player_box src=some_folder/png_resize.php?parametrs=some_parametrs>
Редирект сторінок в РНР
При використанні редиректів варто не забувати примусово завершувати виконання скрипта.
<?
header("Location: www.google.com");
exit;
?>
Інакше потім довго доведеться шукати джерело помилки. По ідеї, це очевидно, але все-таки озвучу ще раз: header лише формує заголовки, які будуть відіслані браузеру, автоматичне завершення виконання скрипта НЕ ВХОДИТЬ в її обов’язки.
Витягнути дробову частину числа на РНР
Можна так:
$t=$t-floor($t);
або так:
$t=substr($t,2,2);
Дехто навіть умудряється використовувати explode()
Повідомлення про помилки в коді
Зрозуміло, що на “бойовому” сервері помилки на очі користувачам виводити не варто. На томість під час розробки без цього – ніяк.
Зручно в конфігураційному файлі створити константу, якій присвоювати значення (показувати чи не показувати помилки) в залежності від того, де виконується код:
<?php
if($_SERVER['REMOTE_ADDR']!='127.0.0.1'){
//інші налаштування....
define('SHOW_ALL_ERRORS', 0);
} else {
//інші налаштування....
define('SHOW_ALL_ERRORS', 1);
}
?>
А потім в коді орієнтуватись на цю константу:
if (SHOW_ALL_ERRORS == 2) {
error_reporting(E_ALL|E_STRICT);//Report all errors plus E_STRICT errors
ini_set('display_errors', 1);
}
if (SHOW_ALL_ERRORS == 1) {
error_reporting(E_ALL ^ E_NOTICE);//Report all errors except E_NOTICE; Also could be E_ALL &amp; ~E_NOTICE
ini_set('display_errors', 1);
}
if (SHOW_ALL_ERRORS == 0) {
ini_set('display_errors', 0);
}