Все про PHP

Самі основи. Як працює PHP.
Сесії. Детальний опис роботи і пояснення механізму.
Хочу вивчати PHP і Mysql. З чого почати?
\ "Лапки \". Складання запитів, слеші, SQL Injection
Регулярні вирази.
Документація по PHP. Мануал по PHP, книги.
Нічого не працює! Що робити??? Пошук помилок та налагодження.
Рішення проблеми "Can not add header information - headers already sent"
Чи не передаються змінні! Проблема Undefined variable
Різниця між абсолютними і відносними шляхами. У файлової системи і на сайті.
Як розбити висновок з mysql посторінково
Визначення IP адреси
Проблеми з кодуванням в MySQL версій 4.1+
Приклад коду, що працює з MySQL
Як писати музику на PHP
Приклад системи управління сайтом
Mail injection. Робота з e-mail засобами PHP.
Безпека PHP скриптів
Базові поняття MySQL і відмінності від текстових файлів.
малювання календаря
Обробка помилок, частина 1. Загальні принципи.
Обробка помилок, частина 2. Розбір прикладу. Винятки.
Що таке PHP?
Як робити UPLOAD файлів на сервер
Як зробити зменшену копію картинки?
шаблони


Самі основи. Як працює PHP.

Відмінність веб-додатки від звичайної програми
Як працює РНР, де він виконується?
Як передати змінну з PHP в JavaScript і назад?
Способи спілкування браузера з сервером.
Перегляд обміну HTTP заголовками
ДУЖЕ ВАЖЛИВЕ ЗАУВАЖЕННЯ
Коментарі

Відмінність веб-додатки від звичайної програми
Починаючи писати програми для вебу, багато початківці програмісти стикаються з такою помилкою. Вони розглядають систему браузер-сервер, як звичайна програма. Інтерактивне. Натиснув кнопку - система зреагувала. Провів мишкою - зреагувала. Вся інформація, яка доступна клієнту - доступна і програмі, програма весь час знаходиться в пам'яті.
Так ось, в веб-програмуванні це не так! .
У момент, коли користувач бачить перед собою сторінку і починає здійснювати якісь дії з нею, PHP вже завершив роботу! І користувач взаємодіє ні з PHP скриптом, а зі своєю сторінкою HTML, яку він отримав в браузер. Результатом роботи скрипта на PHP в більшості випадків є звичайний текст. Текст HTML сторінки. Яка віддається браузеру і показується їм, як звичайний HTML. Ви самі можете в цьому переконатися, написавши в скрипті
<? echo "Привет, <b>Вася!</b>" ; ?> ;
А потім переглянувши в браузері вихідний текст отриманої сторінки. Ніяких тегів PHP там немає! тільки
Привет, <b>Вася!</b>
Тому, що PHP виповнюється на сервері!


Сервер і браузер спілкуються, посилаючи один одному запити по особливому протоколу - HTTP . З'єднання може ініціювати тільки браузер. Він посилає серверу запит - показати такий-то файл. Сервер клієнтові файл посилає.
Тільки так і відбувається. Клієнт запросив - сервер віддав. І забув одразу про клієнта. Звідси стає зрозумілим відповідь на питання, чи можна точно дізнатися, скільки користувачів сечас на сайті. Не можна. тому, що "на сайті" немає жодного. Вони з'єднуються, запитують сторінку, і від'єднуються. Не мають постійного З'єднання з сервером, як, наприклад, гравці в Кваку. Дізнатися можна тільки приблизно, записуючи час кожного з'єднання і вибираючи записи за певний проміжок часу.

Так само, звідси стає ясно, що сервер може дізнатися про клієнта дуже мало. Тільки те, що клієнт надішле в HTTP-запиті. Дозволи екрану там немає ;-)
Все, що сервер може знати про клієнта, можна подивитися командою phpinfo()

Приклад спілкування браузера з сервером:
Користувач натискає на посилання, браузер посилає запит серверу і чекає відповіді:
Браузер -> PHP

PHP виконує скрипт, віддає результат в браузер і завершує роботу:
PHP -> браузер

Браузер відображає сторінку, "переглядаючи" її на предмет посилань, які треба запитати у сервера (теги <img src>, <script src> і так далі) і посилає відповідні запити. Їх можна побачити, переглядаючи обмін заголовками, про що мова буде трохи нижче:
Браузер -> сервер, браузер -> сервер, браузер -> сервер ...

Користувач заповнює форму і натискає на кнопку:
Браузер -> PHP

PHP обробляє форму, записує дані в базу і посилає браузеру заголовок
Location:
PHP -> браузер

Браузер, отримавши цей заголовок, запитує зазначену сторінку
Браузер -> PHP

PHP виконує її ... і так далі.

Як працює РНР, де він виконується?
РНР виконується на сервері. Браузер посилає серверу запит на сторінку з php кодом. Сервер віддає цю сторінку на виконання інтерпретатора PHP, інтерпретатор генерує HTML код, віддає серверу, а сервер посилає клієнтові. Ніякого РНР коду в браузер не потрапляє (це важливо! Це значить, що побачити вихідний код PHP скрипта неможливо!). Єдиний спосіб відправити щось скрипту - це клікнути по посиланню або натиснути на кнопку в формі. Так, щоб РНР обробляв якісь дії користувача в браузері - неможливо. РНР залишився на сервері, чекати нових запитів з даними для обробки. PHP, але не скрипт! Скрипт, який виконувався, віддаючи користувачеві сторінку, завершив роботу. Всі дані, які були в ньому - пропали. Саме тому, якщо якась змінна потрібна при наступних викликах скрипта, її треба цього скрипту передати знову.

Дуже якісна і детальна стаття про основи веб-програмування знаходиться на сайті PHPWIKI.RU. Обов'язково прочитайте її.

Як передати змінну з PHP в JavaScript і назад?
Почнемо з того, що ніяку змінну передати, звичайно ж, неможливо. Оскільки змінна - це частина програми. І з однієї в іншу передати її нальзя. Передати можна тільки значення змінної. Тобто текст. Тобто, відмінності між "передачею змінної в яваскрипт" і формуванням html таблиці НЕМАЄ!
Звідси висновок - "Передати змінну" в Javascript дуже легко. Особливо, повторюся, якщо врахувати, що ніякої "передачі" не відбувається. PHP просто напросто генерує яваскрипт точно так же, як і всю іншу сторінку, разом з усіма змінними.
Точно так же, як ви виводите в браузер рядок "Hello World, це Вася Пупкін!", Виводиться і будь яваскрипт, з усіма своїми змінними.
Єдина умова - ви повинні уявляти собі той яваскрипт, який хочете отримати.
Наприклад в PHP є змінна $name = "Вася" $name = "Вася" , Значення якої треба передати в яваскрипт, щоб отримати
<script>name="Вася";</script>
Ми просто пишемо
<?
$name
= "Вася" ;
?>
<script>name=" <? echo $name ; ?> "</script>

Тобто, фактично, ми просто сформували нашим PHP скриптом якийсь текст, який виглядає, як потрібний нам код на Яваскрипт. Або, з іншого боку, ми писали свій яваскрипт, в потрібних місцях вставляючи висновок змінних з PHP.
Щоб не збожеволіти від різноманітних лапок, настійно рекомендується яваскрипт виводити не весь за допомогою echo, а саме так, як написано тут - закривши тег PHP і відкриваючи їх тільки там, де потрібно вивести змінну.

Як передати змінну з Яваскрипт в PHP?
Точно так же, як і будь-які інші дані - надіславши запит на сервер.
Але треба чітко розуміти, що під час виконання php скрипта отримати що-небудь з Яваскрипт, зрозуміло, неможливо. Передати можна буде тільки при наступному запиті. І обробляти його буде вже інший PHP скрипт.
Якщо треба але події onClick рбратіться до бази даних, то слід пам'ятати, що вона знаходиться на сервері. Тобто, треба запитувати сервер, який запустить PHP скрипт, який звернеться до бази, отримає від неї відповідь і передасть його в браузер.

Все вищевикладене не суперечить, зрозуміло, модною технології асинхронного спілкування браузера з сервером. Всі методи залишилися ті ж, просто спілкування з сервером виконує не сам браузер, а програма на Яваскрипт.
Детальніше можна почитати на ресурсах, присвячених Яваскрипт. З точки зору PHP запити за технологією AJAX нічим не відрізняються від звичайних.

Способи спілкування браузера з сервером.
Способів, що надаються протоколом HTTP, небагато. Це важлива інформація. Ніяких інших способів немає. На практиці використовуються два:
GET - це коли дані передаються в адресному рядку, наприклад, коли користувач тисне посилання.
POST - коли він натискає кнопку в формі.
Сформували сторінку з посиланням або з формою методом GET - запит прийде GET-му. Сформували з формою, в якій вказано метод POST - прийде POST-му.
Визначити, який спосіб слід застосовувати, дуже просто. Якщо форма служить для запиту якоїсь інформації, наприклад - при пошуку, то її слід відправляти методом GET. Щоб можна було оновлювати сторінку, можна було поставити закладку і чи послати посилання одному.
Якщо ж в результаті відправки форми дані записуються або змінюються на сервері, то слід їх відправляти методом POST, причому обов'язково після обробки форми треба перенаправити браузер методом GET. Так само, POST може знадобитися, якщо на сервер треба передати великий обсяг даних (у GET він сильно обмежений), а так само, якщо не слід "світити" передані дані в адресному рядку (при введенні логіна і пароля, наприклад). Але в будь-якому випадку, після обробки POST треба завжди перенаправляти браузер на якусь сторінку, нехай ту ж саму, але вже без даних форми, щоб при оновленні сторінки вони не записувалися повторно. наприклад:
header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'REQUEST_URI' ]);
exit;


Найголовніше, що треба пам'ятати: сервер за своєю ініціативою звернутися до клієнта не може. Ми можемо тільки по факту запиту видати щось браузеру - або сторінку, або команду запросити інший ресурс.

Корисна інформація може міститися в різних НТТР заголовках.
Cookie - якщо сервер поставив куку, і вона не застаріла, то браузер відсилає її разом з кожним запитом.
HTTP authentication - якщо сервер запитував HTTP авторизацію, то браузер при кожному зверненні шле введені логін і пароль.

РНР може посилати HTTP заголовки двома командами - header() і setcookie() .

Перегляд обміну HTTP заголовками
Я дуже рекомендую попрактикуватися з HTTP заголовками, подивитися, як ними обмінюються сервер і клієнт.
Для цього є безліч різних способів. Якщо у вас стоїть популярний download manager FlashGet, то можна використовувати його. Так само заголовки показує популярна програма Proxomitron, можна скачати якісь спеціальні утиліти.
Для IE можна запропонувати плагін http://blunck.se/iehttpheaders/iehttpheaders.html
Для браузера Mozilla є зручний плагін http://livehttpheaders.mozdev.org/
Так само, існує багато інших утиліт, легко знаходять в мережі за запитом HTTP sniffer.
Обов'язково скористайтеся будь-яким способом подивитися HTTP заголовки, якими обмінюється браузер з сервером. Це дуже хороша практика, а так само перевірка - що шле твій скрипт. Зручно при налагодженні установки кук або проблеми з сесіями.
Приблизне уявлення про тих, хто прийшов заголовках можна також отримати, скориставшись функцією getallheaders() . Але слід враховувати, що працює вона тільки якщо PHP зібраний, як модуль.

ДУЖЕ ВАЖЛИВЕ ЗАУВАЖЕННЯ
З того факту, що PHP виповнюється на сервері, і посилає результат своєї роботи браузеру, слід один простий, але дуже важливий висновок. Що PHP в принципі НЕ МОЖЕ відобразити в браузері нічого такого, що неможливо було б зробити засобами html.
Перш, ніж щось писати на PHP - спробуйте це зробити чистим HTML.
"Натискання на Ентер» не переводить рядок? А в html ви не пробували таким чином рядки переводити? Не вийшло? Що за прикрість. Прочитайте, як в html зробити новий рядок і приходьте знову.

PHP в результаті своєї роботи формує не картинку з текстами, як ви її бачите на екрані монітора! PHP формує HTML код! І цей код ЗНАЧНО відрізняється від того зображення, яке ви бачите на екрані. Якщо у вас щось не виходить, то треба завжди дивитися саме ВИХІДНИЙ код сторінки, а не те, як вам її малює браузер. У браузері Internet Explorer вихідний код можна подивитися, вибравши в меню Вид - Перегляд HTML-коду.
Якщо у вас не працює яваскрипт, сформований PHP скриптом, або html показує не те, що ви хочете, то виправити цю проблему дуже просто.
1. Спочатку пишете потрібний яваскрипт або html руками. Якщо у вас з цим проблеми - зверніться в соотвествующий форум - по яваскрипт або html. PHP тут ні до чого.
2. Порівнюєте з тим, що отримано з PHP
3. Вносьте виправлення в PHP скрипт, щоб текст, що віддають їм, не відрізнявся від написаного руками.

Браузер не вміє показувати файли, в які напханий одночасно і html картинки. Браузер уміє показувати тільки відомі йому типи даних. Зокрема, це АБО html АБО картинка. Але не разом. Якщо картинка - то ОДНА. Кілька картинок поспіль браузер показувати не вміє. Браузер уміє показувати HTML, в якому прописані ПОСИЛАННЯ на кілька картинок.
Будь ласка, перш, ніж вивчати PHP - вивчіть хоча б основи HTML! Перш, ніж щось вимагати від PHP - спробуйте зробити це на html.



На початок розділу Вгору


Сесії. Детальний опис роботи і пояснення механізму.

Вступ
Як влаштовані, і як працюють сесії?
Галузь застосування.
Можливі проблеми і їх усунення.
Безпека
Додаткова інформація:
Приклад авторизації за допомогою сесій
ОПС! Дуже Корисні Посилання:
Коментарі

Вступ
Сесії - це насправді дуже просто.
Треба тільки розуміти, для чого вони потрібні і як влаштовані.
Відповімо спочатку на перше питання.
Як показано у відповідному розділі цього FAQ , веб-сервер не підтримує постійного з'єднання з клієнтом, і кожен запит обробляється, як новий, без будь-якого зв'язку з попередніми.
Тобто, не можна ні відстежити запити від одного і того ж самого відвідувача, ні зберегти для нього змінні між переглядами окремих сторінок. Ось для вирішення цих двох завдань і були винайдені сесії.
Власне, сесії, якщо в двох словах - це механізм, що дозволяє однозначно ідентифікувати браузер і створює для цього браузера файл на сервері, в якому зберігаються змінні сеансу.

Детально розписувати нужду в такому механізмі я не буду. Це такі хрестоматійник випадки, як корзина покупок в е-магазині, авторизація, а так само, і не зовсім тривіальні проблеми, такі, наприклад, як захист інтерактивних частин сайту від спаму.

В принципі, досить нескладно зробити власний аналог сесій, не такий функціональний, як вбудований в PHP, але схожий по суті. На куках і базі даних.
При запиті скрипта дивимося, чи прийшла кука з певним ім'ям. Якщо куки немає, то ставимо її і записуємо в базу новий рядок з даними користувача. Якщо кука є, то читаємо з бази дані. Ще одним запитом видаляємо з бази старі записи і ось у нас готовий механізм сесій. Зовсім нескладно. Але є деякі нюанси, які роблять кращим використання саме вбудованого механізму сесій.

Як влаштовані, і як працюють сесії?
Для початку треба якось ідентифікувати браузер. Для цього треба видати йому унікальний ідентифікатор і попросити передавати його з кожним запитом. Соромно зізнатися, але коли я вперше дізнався про сесіях, я думав, що це якийсь особливий механізм, якийсь новий спосіб спілкування браузера з сервером - "сесії". Що ідентифікатор сесії передається якимось особливим чином. Розчарування було жорстоким.
Сесії використовують стандартні, добре відомі способи передачі даних. Власне, інших-то просто і немає.
Ідентифікатор - це звичайна змінна. За замовчуванням її ім'я - PHPSESSID.
Завдання PHP відправити її браузеру, щоб той повернув її з наступним запитом. З уже згадуваного розділу FAQ ясно, що змінну можна передати тільки двома способами: в куках або POST / GET запитом.
PHP використовує обидва варіанти.
За це відповідають два налаштування в php.ini:
session.use_cookies - якщо дорівнює 1, то PHP передає ідентифікатор в куках, якщо 0 - то ні.
session.use_trans_sid якщо дорівнює 1, то PHP передає його, додаючи до URL і формам, якщо 0 - то ні.
Міняти ці та інші параметри сесій можна так само, як і інші настройки PHP - в файлі php.ini, а так само за допомогою команди ini_set() або в файлах настройки веб-сервера

Якщо включена тільки перша, то при старті сесії (при кожному виклику session_start () session_start () ) Клієнту встановлюється кука. Браузер справно при кожному наступному запиті цю куку повертає і PHP має ідентифікатор сесії. Проблеми починаються, якщо браузер куки не повертає. У цьому випадку, не отримуючи куки з ідентифікатором, PHP буде весь час стартувати нову сесію, і механізм працювати не буде.

Якщо включена тільки друга, то кука не виставляються. А відбувається те, заради чого, в основному, власне, і варто використовувати вбудований механізм сесій. Після того, як скрипт виконує свою роботу, і сторінка повністю сформована, PHP переглядає її всю і дописує до кожного посилання і до кожної форми передачу ідентифікатора сесії. Це виглядає приблизно так:
<a href="/index.php">Index</a> перетворюється в
<a href="/index.php?PHPSESSID=9ebca8bd62c830d3e79272b4f585ff8f">Index</a>
а до форм додається приховане поле
<input type="hidden" name="PHPSESSID" value="00196c1c1a02e4c37ac04f921f4a5eec" />
І браузер при кліці на будь-яке посилання, або при натисканні на кнопку в формі, пошле в запиті потрібну нам змінну - ідентифікатор сесії!
З очевидних причин ідентифікатор додається тільки до відносних посиланнях.

Теоретично, в наших з вами саморобних сесіях на куках і базі, можна самому, руками приписати до всіх посиланнями передачу ід - і тоді наші власні сесії працюватимуть незалежно від кук. Але, погодьтеся - приємніше, коли цю роботу робить хтось інший? ;-)

За замовчуванням в останніх версіях PHP включені обидві опції. Як PHP надходить в цьому випадку? Кука виставляється завжди. А посилання автодополняются тільки якщо РНР не виявлено куку з ідентифікатором сесії. Коли користувач в првие раз за цей сеанс заходить на сайт, йому ставиться кука, і доповнюються посилання. При наступному запиті, якщо куки підтримуються, PHP бачить куку і перестає доповнювати посилання. Якщо куки не працюють, то PHP продовжує справно додавати ід до посилань, і сесія не втрачається.
Користувачі, у яких працюють куки, побачать довге посилання з ід тільки один раз.

Фух. З передачею ідентифікатора закінчили.
Тепер залишилося прив'язати до нього файл з даними на стороні сервера.
PHP це зробить за нас. Досить просто написати
session_start ();
$_SESSION [ 'test' ]= 'Hello world!' ;

І PHP запише в файл, пов'язаний з цією сесією, змінну test.
Тут дуже важливе зауваження.
Масив $_SESSION - особливий.
У ньому, власне, і знаходяться змінні, які ми ходимо зробити доступними в різних скриптах.
Щоб помістити змінну в сесію, досить привласнити її елементу масиву $ _SESSION.
Щоб отримати її значення - досить звернутися до того ж елементу. Приклад буде трохи нижче.

Збирання сміття - видаленням застарілих файлів PHP теж займається сам. Як і кодуванням даних і купою всяких інших потрібних речей. В результаті цієї турботи робота з сесіями виявляється дуже простий.
Ось ми, власне, і підійшли до прикладу роботи сесій.
Приклад дуже маленький:
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз. " ;
echo
"<br><a href=" . $_SERVER [ 'PHP_SELF' ]. ">обновить" ;
?>

Ми перевіряємо, чи є у нас в сесії змінна counter, якщо немає, то створюємо її зі значенням 0, а далі виводимо її значення і збільшуємо на одиницю. Збільшене значення запишеться в сесію, і при наступному виклику скрипта змінна буде мати значення 1, і так далі.
Все дуже просто.

Для того, щоб мати доступ до змінних сесії на будь-яких сторінках сайту, треба написати ТІЛЬКИ ОДНУ (!) Сходинку на самому початку КОЖНОГО файлу, в якому нам потрібні сесії:
session_start ();
І далі звертатися до елементів масиву $ _SESSION. Наприклад, перевірка авторизації буде виглядати приблизно так:
session_start ();
if (
$_SESSION [ 'authorized' ]<> 1 ) {
header ( "Location: /auth.php" );
exit;
}


Видалення змінних з сесії.
Якщо у вас register_globals=off , то досить написати
unset( $_SESSION [ 'var' ]);
Якщо ж ні, то тоді поруч з нею треба написати
session_unregister ( 'var' );

Галузь застосування.
Дуже важливо розуміти, для чого сесії варто використовувати, а для чого - ні.

По-перше, пам'ятайте, що сесії можна застосовувати тільки тоді, коли вони потрібні самому користувачеві, а не для того, щоб чинити йому перешкоди. Адже він в будь-який момент може позбутися ідентифікатора!
Скажімо, під час перевірки на те, що заповнює форму людина, а не скрипт, користувач сам зацікавлений в тому, щоб сесія працювала - інакше він не зможе відправити форму! А ось для обмеження кількості запитів до скрипту сесія вже не годиться - зловмисний скрипт просто не буде повертати ідентифікатор.

По-друге. Важливо чітко собі уявляти той факт, що сесія - це сеанс роботи з сайтом, так як його розуміє людина. Прийшов, попрацював, закрив браузер - сесія завершилася. Як сеанс в кіно. Хочеш подивитися ще один - купуй новий квиток. Стартуй новий сеанс. Цьому є і технічне пояснення. Гарантовано механізм сесій працює тільки саме до закриття браузера. Адже у клієнта можуть не працювати куки, а в цьому випадку, природно, все доповнені ідентифікатором посилання пропадуть з його закриттям.
Правда, сесія може пропасти і без закриття браузера. В силу обмежень, розглянутих в найголовнішому розділі цього FAQ, механізм сесій не може визначити той момент, коли користувач закрив браузер. Для цього використовується таймаут - заздалегідь визначений час, після закінчення якого ми вважаємо, що користувач пішов з сайту. За замовчуванням цей параметр дорівнює 24 хвилинам.
Якщо ви хочете зберігати призначену для користувача інформацію на більш тривалий термін, то використовуйте куки і, якщо треба - базу даних на сервері. Зокрема, саме так працюють всі популярні системи авторизації:
- За фактом ідентифікації користувача стартує сесія і ознака авторизованого передається в ній.
- Якщо треба "запам'ятати" користувача, то йому ставиться кука, його ідентифікує.
- При наступному заході користувача на сайт, для того, щоб авторизуватися, він повинен або ввести пароль, або система сама його впізнає по поставленої раніше Кука, і стартує сесію. Нову сесію, а не продовжуючи стару.

По-третє, не варто стартувати сесії без розбору, кожному хто входить на сайт. Це створить абсолютно зайве навантаження. Не використовуйте сесії через дрібниці - наприклад, в лічильниках. Те, що спайлог називає сесіями, вважається, звичайно ж, на основі статистики заходів, а не за допомогою механізму сесій, аналогічного пхп-шному.
До того ж, візьмемо пошуковик, який індексує ваш сайт. Якщо пошуковий робот не підтримує куки, то пхп за замовчуванням буде поставляти до посилань PHPSESSID, що - согласістесь - може не сильно сподобається пошуковику, який, за чутками, і так-то динамічні посилання не шанує, а тут взагалі при кожному заході - нова адреса !
Якщо сесії використовуються для обмеження доступу в Закриту частину сайту, то все просто пошуковик і не повинен його індексувати.
Якщо ж доводиться показувати одну і ту ж сторінку як авторизованим, так і не авторизованим користувачам, то тут допоможе такий трюк - стартувати сесію тільки тим, хто ввів пароль, або тим, у кого вже стартувала сесія.
Для цього в початок кожної сторінки замість просто session_start () session_start () пишемо
if (isset( $_REQUEST [ session_name ()])) session_start ();
таким чином, Ми стартуємо сесію тільки тим, хто надіслав ідентифікатор.
Відповідно, треба ще в перший раз відправити його користувачеві - в момент авторизації.
Якщо ім'я і пролити вірні - пишемо session_start () session_start () !

Можливі проблеми і їх усунення.

Найпоширенішими помилками, які видає РНР при спробі працювати з сесіями, є такі:
Дві з них,
Warning: Cannot send session cookie - headers already sent
Warning: Cannot send session cache limiter - headers already sent

викликані однієї і тієї ж причиною, рішення описано в цьому Пекаха тут
третя,
Warning: open(/tmp\sess_SID, O_RDWR) failed: No such file or directory (2) in full_script_path on line number (раніше вона виглядала, як Warning: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/tmp) ),
якщо перевести її з англійської, докладно пояснює проблему: недоступний зазначений в php.ini шлях до каталогу, в який пишуться файли сесій. Цю помилку виправити найпростіше. Просто прописати каталог, який існує, і доступний на запис, наприклад,
session.save_path = c:\windows\temp
І не забути перезавантажити апач після цього.

Як з'ясовується, кмітливість людська не має меж, і тому я змушений пояснити:
повідомлення про третю помилку (неможливо знайти каталог) НЕМИНУЧЕ призведе до появи перших двох, оскільки повідомлення про помилку - це висновок в браузер і після нього заголовками користуватися не можна. Тому не поспішайте шукати передчасний висновок, а спочатку пропишіть правильний шлях!

Наступною за поширеністю проблемою при роботі з сесіями є важка спадщина register_globals. НЕ давайте змінним скрипта імена, що збігаються з індексами масиву $ _SESSION!
При register_globals = on значення будуть перезаписувати один одного, і ви заплутаєтеся.
А при register_globals = off з'явиться інша помилка: "Your script possibly relies on a session side-effect which existed until PHP 4.2.3.", В разі, якщо в скрипті є змінна сесії не має значення, і глобальна змінна з тим же ім'ям . Щоб від неї позбутися, треба завжди ініціалізувати змінні перед використанням (або хоча б перевіряти на існування) і не давати глобальним змінним імена, що збігаються з індексами масиву $ _SESSION.

Якщо не працює, але і ніяких повідомлень не виводиться, то додайте в самий початок скрипта два рядки, що відповідають за виведення ВСІХ помилок на екран - цілком можливо, що помилки є, але ви їх просто не бачите.
ini_set ( 'display_errors' , 1 );
error_reporting ( E_ALL );

або дивіться помилки в error_log. Взагалі, тема відображення повідомлень про помилки виходить за рамки даної статті, тому просто переконайтеся хоча б, що ви можете їх бачити. Трохи продробнее про пошук помилок можна прочитати в <?
session_start ();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз.<br>
<a href="
. $_SERVER [ 'PHP_SELF' ]. '?' . session_name (). '=' . session_id (). ">обновить</a>" ;
?>
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз.<br>
<a href="
. $_SERVER [ 'PHP_SELF' ]. '?' . session_name (). '=' . session_id (). ">обновить</a>" ;
?>

При цьому слід переконатися, що не включена директива session.use_only_cookies , яка забороняє PHP приймати ідентифікатор сесії, якщо він був переданий через URL

Якщо цей приклад не запрацює, то проблема або в банальних друкарських помилках (половина "проблем" з сесіями походить від неправильно написаного імені змінної), або в занадто старої версії PHP: підтримка сесій з'явилася у версії 4.0, а масив $_SESSION - в 4.1 (До цього використовувався $HTTP_SESSION_VARS ).
Якщо ж запрацює - то проблема в куках. Відстежуйте - що за куку ставить сервер браузеру, чи повертає браузер її. Шукати дуже корисно, переглядаючи переглядаючи обмін HTTP-заголовками між браузером і сервером.
Пояснення принципу роботи кук виходить за рамки цього і так аж надто великого тексту, але хоча б переконайтеся, що сервер куку з ідентифікатором посилає, а браузер - повертає. І при цьому ідентифікатори збігаються один з одним =)
Установка куки повинна виглядати, як
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;
або як
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; path=/
(Якщо ви запитуєте скрипт не з кореневого каталогу)
Відповідь сервера повинен виглядати, як
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6
або
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b
якщо браузер повертає інші куки, крім ідентифікатора сесії.

Якщо браузер куки не повертає - перевірте, чи працюють куки взагалі.
Переконайтеся, що домен, до якого ви звертаєтеся, має нормальне ім'я (в якому є хоча б одна точка і не міститься заборонених символів, наприклад підкреслення) і почистіть кеш браузера - це дві основні причини, по котрому куки можуть не працювати.

Якщо приклад звідси працює, а ваш власний код - немає, то проблема, очевидно, не в сесіях, а в алгоритмі. Шукайте, де втратили змінну, по кроках переносите приклад звідси, <a href=>, але не робить цього для header-ів, яваскрипт, мета-тегів.
Тому треба додавати ідентифікатор руками, наприклад, так:
header ( "Location: /script.php?" . session_name (). '=' . session_id ());

Слід пам'ятати, що пхп лочіт файл сесії. Тобто, якщо один ваш скрипт стартує сесію і довго виконується, а інший намагається в цей час стартувати її з тим же ідентифікатором, то він зависне. Тому в довго виконуються скриптах слід стартувати сесію тільки тоді, коли вона потрібна, і тут же закривати її, за допомогою session_write_close()

Так само, дуже рідкісна, і зовсім незрозуміло, звідки з'являється, проблема буває в тому, що настройка session.save_handler має значення, відмінне від files. Якщо це не так - виправляйте.

Безпека
Безпека сесій - тема велика. Тому зупинюся на декількох основних моментах.
Самий хрестоматійний - не передавати ідентифікатор через адресний рядок. Про це написано навіть в php.ini, але це обмежує функціональність сесій. Якщо ви вирішите піти цій раді, то крім session.use_trans_sid = 0 не забудьте session.use_only_cookies = 1
Бажано прив'язувати сесію до IP адресою: таким чином, якщо ідентифікатор буде вкрадений, то лиходій все одно не зможе ним скористатися в більшості випадків.
Рекомендується користуватися директивою session.save_path, за допомогою якої задати власний каталог для збереження файлів сесій. Це більш безпечно, ніж коли вони зберігаються в загальному тимчасовому каталозі сервера за замовчуванням.

Додаткова інформація:
  • Крім кук, механізм сесій посилає ще й заголовки, що забороняють кешування сторінок (той самий cache limiter). Для html це правильно і необхідно. Але от коли ви намагаєтеся скриптом, перевіряючим авторизацію, віддати файл, то інтернет експлорер відмовляється його завантажувати. Саме через це заголовка. виклик
    session_cache_limiter ( "private" );
    перед стартом сесії має вирішити проблему.
  • Як це не здається дивним, але в масиві $_SESSION не можна використовувати числові індекси - $_SESSION [ 1 ], $_SESSION [ '10' ] $_SESSION [ 1 ], $_SESSION [ '10' ] - Сесія працювати не будуть.
  • Десь між версіями 4.2 і 5.0 неможливо було встановити session.use_trans_sid за допомогою ini_set () ini_set () . Починаючи з 5.0 вже можна знову.
  • До версії 4.3.3 куку PHP відправляв куку тільки якщо при старті сесії в запиті отсутстввал ідентифікатор. Тепер же кука надсилається при кожному виклику session_start () session_start ()

    Приклад авторизації за допомогою сесій
    Проілюструємо все вищенаписане невеликим прикладом:
    створимо файл auth.php:
    <?
    if (isset( $_POST [ 'auth_name' ])) {
    $name = mysql_real_escape_string ( $_POST [ 'auth_name' ]);
    $pass = mysql_real_escape_string ( $_POST [ 'auth_pass' ]);
    $query = "SELECT * FROM users WHERE name='$name' AND pass='$pass'" ;
    $res = mysql_query ( $query ) or trigger_error ( mysql_error (). $query );
    if (
    $row = mysql_fetch_assoc ( $res )) {
    session_start ();
    $_SESSION [ 'user_id' ] = $row [ 'id' ];
    $_SESSION [ 'ip' ] = $_SERVER [ 'REMOTE_ADDR' ];
    }
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'REQUEST_URI' ]);
    exit;
    }
    if (isset(
    $_GET [ 'action' ]) AND $_GET [ 'action' ]== "logout" ) {
    session_start ();
    session_destroy ();
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. "/" );
    exit;
    }
    if (isset(
    $_REQUEST [ session_name ()])) session_start ();
    if (isset(
    $_SESSION [ 'user_id' ]) AND $_SESSION [ 'ip' ] == $_SERVER [ 'REMOTE_ADDR' ]) return;
    else {
    ?>
    <form method="POST">
    <input type="text" name="auth_name"><br>
    <input type="password" name="auth_pass"><br>
    <input type="submit"><br>
    </form>
    <?
    }
    exit;
    ?>


    тепер достатньо написати в усіх захищаються скриптах рядок
    require "auth.php";

    ОПС! Дуже Корисні Посилання:
    http://www.php.net/manual/ru/ref.session.php - найостанніша і свіжа інформація про підтримку сесій в PHP в офіційній документації, плюс численні коментарі користувачів. Настійно рекомендується до прочитання.
    http://phpclub.ru/manrus/f/ref.session.html - ВЕСЬМА застарілий переклад цієї глави на російський, з документації в перекладі Олександра Пірамідіна.
    http://phpclub.ru/detail/article/sessions
    Стаття з пафосною назвою "Правда про сесіях". Двоїсте враження залишає. Спочатку автор ДУЖЕ доступно розповідає про механізм сесій, але методи, які він пропонує до кінця статті - зовсім каламутні.

    Хрестоматійна стаття Дмитра Бородіна з сайту
    http://php.spb.ru/ настійно НЕ рекомендується.
    Хлопці, вона страшно застаріла. Мало того, що в ній є фактичні неточності, так з сесіями в PHP вже давно просто не працюють.
    Величезне Дімі спасибі за неї, це була перша стаття по сесіях російською мовою, я сам по ній вчився, але зараз треба її відправити на заслужений відпочинок.
    Так само, застаріли на жаль, і багато інших статті, що лежать в інтернеті і не оновлювалися роками.


    На початок розділу Вгору


    Хочу вивчати PHP і Mysql. З чого почати?

    Інформація для початківців вивчати PHP і MySQL
    Програмне забезпечення.
    Форуми.
    Коментарі

    Інформація для початківців вивчати PHP і MySQL
    Для тих, хто хоче вивчати PHP, можна порадити чудовий " Самовчитель PHP " з сайту PHP5.RU
    Курс знаходиться в процесі написання, але вже зараз посилання на окремі уроки з нього стоять в різних розділах цього FAQ. І, повірте - воно того варте.
    Не можу не порекомендувати чудовий матеріал Вадима Ткаченко АКА Bizon-а "Вступ до PHP і MySQL" . Він навіть видавався окремою книгою, а зараз - виправлений і доповнений - розміщується на сайті
    " PHP в деталях ". Цей ресурс стоїть осібно. На відміну від попередніх, рекомендувати прочитати його повністю може тільки садист - там занадто багато інформації. але в цьому і його принадність. Це невичерпний ресурс інформації по PHP. Єдине зауваження - звертайте увагу на дату написання статті. Не варто особливо довіряти тим, що написані до 2003 року.
    Ну, і, звичайно ж - цей сайт, http://phpfaq.ru
    Якщо ви ще не прочитали його цілком - обов'язково зробіть це. Тут перераховані проблеми, з якими рано чи пізно зіткнеться КОЖЕН, хто пише на PHP.
    Непоганий курс вивчення MySQL:
    http://www.intuit.ru/department/database/mysql/

    Програмне забезпечення.
    Для роботи з РНР під Windows, треба встановити такі програми:
    - Web-сервер Apache (5Mb)
    - Сам PHP (10Mb)
    - За бажанням - MySQL (23Mb).
    Налаштування дуже проста. Апач встановлюється програмою установки. Там, де він запитує ім'я вашого сервера і емейл адміністратора, треба 2 рази написати localhost і свій e-mail.
    PHP розпаковується з ЗІПа в будь-який каталог за бажанням (стандартно - C: \ PHP) і налаштовується обов'язково як модуль Апача. Для цього треба виконати три дії:
    - Переписати файл php5ts.dll в каталог WINDOWS
    - В файл httpd.conf (C: \ Program Files \ Apache Group \ Apache \ conf \ httpd.conf), в самому низу, додати дві строчки
    LoadModule php5_module c:/php/php5apache2_2.dll
    AddType application/x-httpd-php .php .php3 .phtml

    - Перезапустити Апач (ярликом Restart в групі Apache HTTP Server / Configure Apache Server)
    Виконавши всі ці дії, можна покласти тестовий php скрипт (припустимо, він називається test.php і складається з рядка
    <?php phpinfo (); ?>
    ) В каталог, який є кореневим для веб-сервера (за замовчуванням це C: \ Program Files \ Apache Group \ Apache \ htdocs \) і звернутися до нього, написавши в браузері адресу
    http://127.0.0.1/test.php

    MySQL після скачування слід розпакувати з ЗІПа, запустити setup.exe і встановити. Щоб уникнути проблем, КРАЩЕ встановити в папку за замовчуванням - c: \ mysql. Якщо ви цього не зробили, то уважно читайте документацію.
    Після установки в командному рядку (Пуск - Виконати - cmd.exe) виконайте наступні команди:
    C:\mysql\bin\mysqld --install
    net start mysql

    Усе! MySQL встановлена! Для перевірки наберіть
    C:\mysql\bin\mysql -uroot
    якщо консоль запустилася - все працює. Наберіть exit для виходу і приступайте до конфігурації підтримки mysql в PHP.
    Для цього, якщо ви не зробили цього раніше, візьміть файл c:\php\php.ini-recommended і скопіюйте під ім'ям php.ini в каталог windows. Потім внесіть необхідні зміни, прибравши крапку з комою на початку рядка
    ;extension=php_mysql.dll
    а потім перепишіть файли c:\php\libmysql.dll і c:\php\ext\php_mysql.dll в каталог WINDOWS і не забудьте після цього перезапустити Апач, як це було описано вище.
    Тепер ви можете використовувати mysql в своїх php-скрипти.

    Ті, для кого ця інструкція занадто складна, можуть спробувати встановити готовий комплект Денвер-2 .
    У нього входить відразу все, що потрібно, і ще багато непотрібного. А головне - працює все само.
    Ще одна перевага Денвера в тому, що обсяг базового комплекту в 10 разів менше повних версій - всього 4 мегабайта. А так само те, що його автор пише цікаві книжки по PHP.

    Так само, всім допитливим рекомендується ВЕСЬМА розумна стаття Установка і настройка Apache + PHP
    з сайту PHP5.RU. І, звичайно ж - розділи офіційної документації, присвячені установці відповідних програм.

    Форуми.
    При вивченні будь-якої справи обов'язково з'являться питання.
    Питання зручно задавати на форумах.
    http://phpclub.ru/talk/forumdisplay.php?s=&forumid=12
    Форум PHPклуба. Найбільш відвідуваний і відомий. На жаль, популярність служить йому погану службу. Дуже часто на питання новачка відповідає ще більш зелений новачок, даючи абсолютно неправильну відповідь. Однак професіоналів там теж не бракує, готових пояснити помилки і першому і другому.

    PHP представлений і в російськомовному сегменті Livejournal
    У спільнотах ru_php і [Info] ru_mysql завжди знайдуться професіонали, кототорие допоможуть з будь-якою проблемою. Тільки не забудьте спочатку прочитати правила спільноти!

    Ставлячи питання на форумі, пам'ятайте:
    Що, швидше за все, з ним вже стикалася тисяча осіб. І докладні відповіді можна знайти в пошуку. Якщо ж, все-таки, питання доводиться задавати - то потрібно описувати якнайдетальніше (тільки своїми словами, а не кодом!), Що ви робили, що хотіли отримати і що вийшло в результаті, а так же точно копіюйте повідомлення про помилки.

    Сайти для початківців.
    Раніше тут були розміщені посилання на різні сайти від початківців для початківців.
    На жаль, і так якось не блищали якістю матеріалу, вони давно закинуті своїми авторами і остаточно втратили актуальність.
    Все, що є кращого за темою PHP, перераховано у верхній частині сторінки.
    Якщо ви знаєте хороший сайт - напишіть про нього в розділі "Зворотній зв'язок".

    На початок розділу Вгору


    \ "Лапки \". Складання запитів, слеші, SQL Injection

    Швидкі рекомендації.
    Правила складання запитів MySQL
    Динамічне складання запитів
    Правильна робота з спецсимволів при складанні запитів
    підготовлені вираження
    SQL Injection
    Особливості роботи з оператором LIKE
    Про Слеш. Як від них позбутися
    зауваження
    ОПС: дуже корисні посилання:
    Примітка: форми
    Коментарі

    Швидкі рекомендації.
    Для запобігання SQL ін'єкцій слід дотримуватися два простих правила:
    1. Не поміщати в БД дані без обробки.
    Це можна зробити або за допомогою підготовлених виразів, або обробляючи параметри вручну.
    Якщо запит залишається вручну, то
    - Все числові параметри повинні бути приведені до потрібного типу
    - Всі інші параметри повинні бути оброблені функцією mysql_real_escape_string () і укладені в лапки.

    2. Не поміщати в запит керуючі структури і ідентифікатори, введені користувачем.
    А заздалегідь прописувати в скрипті список можливих варіантів, і вибирати тільки з них.

    Почитати про це докладніше - чому треба робити саме так і як це все працює, можна нижче. І настійно рекомендується.

    Правила складання запитів MySQL
    Для початку - трохи про те, чому взагалі потрібні ці слеші.
    Якщо ми підставляємо в запит будь-які дані, то, щоб відрізнити ці дані від команд SQL, їх треба брати в лапки.
    Наприклад, якщо написати
    SELECT * FROM table WHERE name = Bill
    то база вирішить, що Bill - це ім'я іншого поля, не знайде його, і видасть помилку. Тому що підставляються дані (в даному випадку ім'я Bill) треба брати в лапки - тоді база вважатиме його рядком, значення якої треба привласнити полю name:
    SELECT * FROM table WHERE name = 'Bill'
    Однак, і в самих даних можуть зустрічатися лапки теж. Наприклад,
    SELECT * FROM table WHERE name = 'Д'Артаньян'
    Тут база даних вирішить, що 'Д' - це дані, а Артаньян - команда, яку вона не знає, і теж видасть помилку. Тому і треба прослешівать всі дані, щоб пояснити базі, що зустрічаються в них лапки (і деякі інші спецсимволи) відносяться до даних.
    В результаті ми отримаємо правильний запит, який помилок не викличе:
    SELECT * FROM table WHERE name = 'Д\'Артаньян'

    Таким чином, ми з'ясували, що при підстановці даних в запит, слід дотримуватися двох правил:
    - все вставляемые в запрос данные должны быть заключены в кавычки (одинарные или двойные, но удобнее и чаще используются одинарные).
    - во всех строковых переменных должны быть экранированы слешами спецсимволы.

    Следует специально отметить: добавленные слеши НЕ идут в базу. Они нужны только в запросе. При попадании в базу слеши отбрасываются. Соответственно, распространенной ошибкой является применение stripslashes при получении данных из базы.

    На самом деле, всё вышесказанное относится к данным строкового типа и датам. Числа можно вставлять не прослешивая и не окружaя кавычками. Если вы так делаете, то ОБЯЗАТЕЛЬНО! насильно приводите данные к нужному типу перед вставкой в запрос, например:
    $id = intval ( $id );
    Однако для простоты (и надёжности) можно и с числами работать, как со строками (проскольку mysql всё равно преобразует их к нужному типу). Соответственно, мы будем любые данные, вставляемые в запрос, прослешивать и заключать в кавычки.

    Так же, есть ещё одно правило - необязательное, но его следует придерживаться во избежание появления ошибок:
    Имена полей и таблиц следует заключать в обратные одинарные кавычки - "`" (клавиша с этим символом находится на стандартной клавиатуре слева от клавиши "1") Ведь имя поля может совпадать с ключевыми словами mysql, но если мы используем обратную кавычку, то MySQL поймёт всё правильно:
    SELECT * FROM `table` WHERE `date` = '2006-04-04'
    Следует различать эти кавычки и не путать одни с другими. Следует также помнить, что обратные кавычки слешами не экранируются.

    Динамічне складання запитів
    Если SQL запрос в скрипте написан целиком, и никак не меняется, например
    SELECT * FROM `table`
    то никаких проблем с ним и не будет.
    Но вся сила наших скриптов именно в динамическом составлении запросов!
    Вместо того, чтобы писать готовые запросы на все случаи жизни, мы составляем их на основании поступающих в скрипт данных.
    И вот здесь нас подстерегает опасность.
    Допустим, мы составляем запрос с использованием переменной:
    SELECT * FROM table WHERE name = '$name'
    Вроде бы - всё нормально?
    А если $name у нас будет Д'Артаньян? Запрос выдаст ошибку!
    То есть, переменную перед подстановкой в запрос надо прослешить.
    Это можно сделать несколькими путями.
    Самый простой (и неправильный) - положиться на волшебные кавычки. Как вы уже догадались, именно для этого случая они и были придуманы. Ради того, чтобы уберечь SQL запросы забывчивых программистов от ошибок, ВСЕ поступающие в скрипт данные прослешиваются без разбору.
    Если вы используете чужой код, то лучше воспользоваться волшебными кавычками . Это может создавать некоторые неудобства и не гарантирует вас от ошибок или взлома (поскольку прослешиванием правила составления запросов не исчерпываются) но хотя бы снижает риск. Поэтому, при использовании чужого кода, обязательно убедитесь, что волшебные кавычки включены.
    Если же вы пишете весь код самостоятельно, то следует научиться правильному составлению запросов.

    Правильна робота з спецсимволів при складанні запитів
    Отже. Как мы уже узнали выше, чтобы правильно составить запрос, надо заключать данные в кавычки и прослешивать их.
    С первым всё понятно. При составлении динамических запросов мы никогда не забываем все данные заключить в кавычки:
    $query = "INSERT INTO `table` VALUES(NULL,'$name','$date','$price')" ;
    Если переменная $price должна быть типа int и мы приведём её к этому типу, то можно её не заключать в кавычки. Однако, если заключим, то беды особой не будет, но зато можно будет сделать работу с данными единообразной.

    Второе же - прослешивание - и является тем, ради чего, собственно, по большей части, и написан весь этот текст. Поскольку вызывает больше всего вопросов и затруднений.

    Сначала отключим волшебные кавычки. Так, как это описано в самом начале.
    Почему это следует сделать?
    По многим причинам. Самая очевидная - логическая. "Волшебные кавычки" добавляют слеши не там, где они нужны - при составлении запроса , а еще до попадания в скрипт! Но ведь данные совсем не обязательно после этого будут вставляться в запрос. Может быть, их придётся выводить пользователю, и слеши будут только мешать. Плюс к тому, добавленные слеши помешают, к примеру, правильно проверить длину введённой строки. К тому же, прослешивать нам надо не только пришедшие от пользователя данные, а вообще любые, вставляемые в запрос - многим этот очевидный факт даже не приходил в голову! Список можно продолжать, но вывод один: добавлять слеши надо не автоматом, без разбору, до начала выполнения скрипта, а только там, где действительно надо – при составлении запроса.
    Есть и ещё одна причина: при использовании кодировки Unicode, которая приобретает всё большую популярность, а со временем займёт доминирующее положение в веб, волшебные кавычки могут попросту испортить текст, приняв часть мультибайтной строки за спецсимвол.

    Теперь займёмся добавлением слешей самостоятельно.
    Во-первых, для прослешивания мы воспользуемся функцией mysql_real_escape_string()
    Следует помнить, что применять её можно только после установления соединения с базой.
    Эта функция делает гораздо больше, чем устаревшие addslashes и mysql_escape_string. Во-первых, она облегчает ведение и чтение логов mysql, заменяя, например, символ перевода строки на "\n" и некоторые другие символы на escape-последовательности. Во-вторых, и самое главное - она корректно работает с многобайтными кодировками, принимая во внимание текущую кодировку MySQL и не портит, таким образом, тексты в кодировке Unicode.
    Во-вторых, не забываем, что прослешить надо все те данные, которые мы заключили в запросе в кавычки:

    $name
    = mysql_real_escape_string ( $name );
    $age = mysql_real_escape_string ( $age );
    $query = "INSERT INTO table (name,age,class) VALUES ('$name','$age',11)" ;

    або:
    $query = "SELECT * FROM table WHERE name LIKE '" . mysql_real_escape_string ( $_GET [ 'name' ]). "%'" ;

    Видно, что код получается довольно громоздким. Для облегчения составления запросов можно пуститься на разные ухищнения - сделать функцию для составления запросов из массива (очень удобно для запросов типа INSERT и UPDATE), прослешивать массив данных в цикле, и так далее.
    Вы можете написать и свою библиотеку или функцию для составления запросов.
    Главное - помнить, что только неукоснительное соблюдение правил составления запросов гарантирует вас от взлома БД, а так же сознавать, что применение "волшебных кавычек", при видимой лёгкости составления запросов, не даёт такой гарантии, а только мешает нормальной работе приложения.

    підготовлені вираження
    Есть еще один способ отправлять запросы в БД, называемый "подготовленными выражениями" (prepared statements).
    Суть его заключается в том, что подготавливается шаблон запроса, со специальными маркерами, на место которых будут подставлены динамические компоненты. Пример такого шаблона:
    SELECT * FROM table WHERE name=?
    Знак вопроса здесь - это тот самый маркер. По-другому он называетсй плейсхолдером (placeholder). Весь секрет в том, что данные на его место подставляет специальная функция, которая "привязывает" переменную к запросу.
    Вот как выглядит код в таком случае:
    $stmt = $mysqli -> prepare ( "SELECT District FROM City WHERE Name=?" );
    $stmt -> bind_param ( "s" , $city );
    $stmt -> execute ();

    В первой строчке мы подготавливаем шаблон запроса.
    Во второй - привязываем к маркеру значение переменной $city.
    В третьей строчке выполняем подготовленный таким образом запрос.
    При этом запрос и данные идут в базу не вместе, а по отдельности, исключая возможность какой-либо ошибки или злонамеренной манипуляции.

    Понятно, что возникает много вопросов. Но ни объём, ни тематика данной статьи не позволяют остановиться на них более подробно. Рекомендую обратиться к документации по библиотекам mysqli и PDO , реализующим данный принцип.
    Так же, можно использовать библиотеку DbSimple Дмитрия Котерова или PEAR::DB . Основное отличие этих двух состоит в том, что они реализуют механизм подготовленных выражений только внешне. А внутри работают по-старинке - составляя запрос и отправляя его в базу, беря на себя работу по обработке переменных. А PDO и mysqli работают, как было описано выше - то есть, шаблон запроса и данные уходят в базу по отдельности.

    SQL Injection

    Итак, мы научились правильно подставлять в запрос данные.
    АЛЕ! Динамическое составление запросов не исчерпывается подстановкой данных. Часто нам приходится подставлять в запрос команды SQL и имена полей. И здесь мы уже переходим к теме безопасности:

    SQL Injection - это способ хакерской атаки, когда передаваемые скрипту данные модифицируются таким образом, что запрос, формируемый в этом скрипте, начинает выполнять совсем не то, для чего он предназначался.
    Правила защиты от таких атак можно разделить на два пункта:
    1. Работа с данными.
    2. Работа с управляющими элементами запроса.

    Первый пункт мы подробно рассматривали выше. Он, можно сказать, и не является, собственно, защитой. Соблюдение правил добавления занных в запрос продиктовано, в первую очередь, требованиями СИНТАКСИСА SQL. А как побочный эффект мы имеем и защиту от взлома.

    Второй пункт гораздо сложнее, поскольку не существует такого же единого универсального правила, как для данных - обратная кавычка никак не защитит имя поля от модификации хакером. Невозможно кавычками защитить имя таблицы, операторы SQL, параметры команды LIMIT, и другие операторы.
    Поэтому основное правило при подстановке управляющих элементов в запрос такое:
    Если требуется динамически подставлять в запрос операторы SQL или имена полей, баз данных, таблиц, то ни под каким видом не вставлять их в запрос напрямую.
    Все варианты таких добавлений должны быть ЗАРАНЕЕ прописаны в вашем скрипте и выбираться на основании того, что ввёл пользователь.
    К примеру, если надо передать имя поля в оператор order by, то ни в коем случае нельзя подставлять его напрямую. Надо сначала проверить его. К примеру, сделать массив допустимых значений, и подставлять в запрос только если переданный параметр в этом массиве присутствует:
    $orders =array( "name" , "price" , "qty" );
    $key = array_search ( $_GET [ 'sort' ], $orders ));
    $orderby = $orders [ $key ];
    $query = "SELECT * FROM `table` ORDER BY $orderby" ;

    Мы ищем в массиве заранее описанных вариантов введённое пользователем слово, и, если находим, то выбираем соответствующий элемент массива. Если совпадения не будет найдено, то будет выбран первый элемент массива.
    Таким образом, в запрос подставляется не то, что ввёл пользователь, а то, что было прописано у нас в скрипте.
    Точно так же надо поступать и во всех остальных случаях
    К примеру, если динамически формируется оператор WHERE:
    if (!empty( $_GET [ 'price' ])) $where .= "price='" . mysql_real_escape_string ( $_GET [ 'price' ]). "'" ;
    $query = "SELECT * FROM `table` WHERE $where" ;

    Мне сложно представить себе случай, когда имя таблицы может подставляться в запрос динамически, но если такое случится, то имя тоже надо вставлять только из заранее прописанного в скрипте набора.
    Параметры оператора LIMIT следует принудительно приводить к целочисленному типу с помощью арифметических операций или функции intval ().
    Не следует думать, что перечисленными здесь примерами исчерпываются все варианты динамического составления запросов. Нужно просто понять принцип, и применять его во всех подобных случаях.

    Особенности работы с оператором LIKE
    Совершенно отдельный случай - оператор LIKE.
    Во-первых, помимо обычного прослешивания, в переменных, которые подставляются в LIKE, надо удваивать слеши. То есть, если в переменной содержится символ \, то его надо удвоить, а после этого выполнить обычное прослешивание, через mysql_real_escape_string.
    К примеру, если мы ищем строку
    символ \ называется "backslash"
    и нам нужно точное совпадение, то мы просто применяем mysql_real_escape_string и запрос получается стандартный:
    SELECT * FROM test WHERE field = 'символ \\ называется \"backslash\"'
    Если же мы хотим подставить эту строку в LIKE, то сначала надо заменить каждый слеш на два, а потом применить mysql_real_escape_string. В результате получится
    SELECT * FROM table WHERE field LIKE '%символ \\\\ называется \"backslash\"%'

    Во-вторых, следует обратить внимание на то, что ни одна из функций, добавляющих слеши, не добавляет их к метасимволам поиска "%" и "_", используемым в операторе LIKE. Поэтому, если вы используете этот оператор, и не хотите, чтобы символы _ и % использовались, как маски, то добавляйте слеши вручную. Это можно сделать командой
    $data = addCslashes ( $data , '%_' );
    Внимание - это не addslashes! В имени этой функции есть дополнительная буква "c".

    Таким образом получается, что переменные, используемые в операторе LIKE мы должны обрабатывать отдельно.
    сначала заменять один слеш на два, с помощью такого, к примеру, кода:
    $var = str_replace ( '\\' , '\\\\' , $var );
    затем (можно наравне со всеми другими данными, идущими в запрос) прослешиваем:
    $var = mysql_real_escape_string ( $var );
    а затем, если хотим, чтобы _ и % соответствовали точно самим себе, делаем
    $var = addCslashes ( $var , '_%' );

    В результате, если мы будем искать, к примеру, такую строку
    символ \ называется "backslash", а символ _ называется "underscore"
    то после обработки, в запросе она должна выглядеть так:
    '%символ \\\\ называется \"backslash\", а символ \_ называется \"underscore\"
    То есть, слеш, который был в строке изначально - учетверился. Остальные символы прослешились, как обычно. Плюс - прослешился символ подчёркивания.

    Про Слеш. Як від них позбутися
    Слеш, или бэкслеш, от английского back slash - обратная косая черта ("\"), которая непонятным образом вдруг сама собой появляется в ваших переменных. Добавляется он к некоторым спецсимволам, но в основном его замечают из-за кавычек.
    Происходит это из-за специальных настроек PHP, обычно включённых на хостинге по умолчанию. Теоретически, эти настройки могут повысить безопасность скриптов, работаюющих с БД. Практически же, от автоматического добавления слешей часто получается путаница и неудобство, как при работе с БД, так и при её отсутствии.
    Ниже мы подробно разберём оба этих случая.

    За автоматическое добавление слешей отвечают директивы php.ini, которые носят общее название "волшебные кавычки":
    magic_quotes_gpc и magic_quotes_runtime
    Если включена первая, то PHP автоматически добавляет слеши к данным, пришедшим от пользователя - из POST, GET запросов и кук (а так же - к логину и паролю, полученным через HTTP Authorisation).
    Если вторая, то слеши добавляются к данным, полученым во время исполнения скрипта - например, из файла или базы данных.

    Если вы работаете без базы данных, или же работаете с БД правильно (о чём будет написано ниже), лишние слеши вам только мешают, и от них надо избавляться. Проще и правильнее всего отключить автоматическое добавление, в настройках PHP.
    Это можно сделать либо поправив соответствующие директивы в php.ini, если у вас есть к нему доступ, либо создав в коневом каталоге сайта файл .htaccess , и добавив в него строчки
    php_flag magic_quotes_gpc 0
    php_flag magic_quotes_runtime 0


    Если отключить таким образом не получается, то придётся писать код разной степени сложности, чтобы очистить от слешей входящие данные. (Впрочем, если вы хотите написать переносимое приложение, не зависящее от настроек PHP, то написать его всё равно придётся. И включать отдельным блоком в начале ваших скриптов).

    С данными, получаемыми во время работы, разобраться проще всего: достаточно в начале скрипта написать
    set_magic_quotes_runtime(0);
    Для данных, полученных от пользователя, всё гораздо сложнее. Для этого кода нам потребуется две функции:
  • проверить, добавил ли PHP, можно с помощью функции get_magic_quotes_gpc .
  • удаляет слеши функция stripslashes .
    Соответственно, с помощью первой надо проверить, и, если PHP добавил, то перебрать все входящие переменные и очистить с помощью второй.
    Если вы работаете правильно, при register_globals = off , то достаточно применить stripslashes ко всем массивам, содержащим данные, приходящие из браузера.
    к примеру, можно включить во все скрипты сайта вот такой код:
    function strips (& $el ) {
    if (
    is_array ( $el ))
    foreach(
    $el as $k => $v )
    strips ( $el [ $k ]);
    else
    $el = stripslashes ( $el );
    }
    if (
    get_magic_quotes_gpc ()) {
    strips ( $_GET );
    strips ( $_POST );
    strips ( $_COOKIE );
    strips ( $_REQUEST );
    if (isset(
    $_SERVER [ 'PHP_AUTH_USER' ])) strips ( $_SERVER [ 'PHP_AUTH_USER' ]);
    if (isset(
    $_SERVER [ 'PHP_AUTH_PW' ])) strips ( $_SERVER [ 'PHP_AUTH_PW' ]);
    }

    В случае же неправильных настроек register_globals приемлемое решение и вовсе будет найти затруднительно, поэтому лучше - повторюсь - сразу работать при правильных настройках.

    Замечания
    • Среди причин, по которым не стоит полагаться на "волшебные кавычки", есть ещё одна. Весьма маловероятная, но всё же. К "волшебным кавычкам" относится на самом деле не две директивы, а три. Третья - magic_quotes_sybase . Мало того, что она вместо слеша добавляет кавычку - так она ещё и отменяет действие magic_quotes_gpc. Если каким-то чудом обе эти директивы имеют статус 'on', то последняя не сработает! То есть, полагаясь на "волшебные кавычки", мы в этом случае получим все прелести неправильно составленных запросов. Вообще, чисто теоретически, надо учитывать наличие этой директивы, поскольку она преподносит ещё и такой сюрприз, как... изменение поведения функций addslashes и stripslashes! Если magic_quotes_sybase = on , то эти функции начинают вместо слеша добавлять и удалять одинарную кавычку соответственно.
    • Все приведенные примеры касаются только БД Mysql. Конкретные правила составления запросов могут отличаться для других СУБД, но общий принцип остается прежним:
      • если API для работы с БД или сторонняя библиотека предоставляет специальные функции для составления запросов , и есть возможность их использования, то пользоваться в первую очередь надо ими.

      • если таких функций нет, то следует искать в документации функции экранирования спецсимволов для этой СУБД.


    ОПС: очень полезные ссылки:
    Про волшебные кавычки на сайте PHP.NET
    Про SQL-инъекции на сайте PHP.NET

    Несколько материалов по SQL Injection:
    http://www.securitylab.ru/45438.html
    http://www.securitylab.ru/49424.html
    http://www.nextgenss.com/papers/advanced_sql_injection.pdf

    Примітка: форми
    При выводе value в тегах input форм, слеши не помогают.
    Чтобы текст в таком поле выводился целиком, value надо заключать в кавычки , а к выводимым данным применять функцию htmlspecialchars()
    приклад:
    <input type="text" name="name" value=" <? echo htmlspecialchars ( $name , ENT_QUOTES ) ?> ">
    <textarea> <? echo htmlspecialchars ( $text , ENT_QUOTES ) ?> </textarea>

    Необходимо так же отметить (хоть это уже совсем не имеет отношения к кавычкам и слешам), что функцию htmlspecialchars следует применять при выводе в браузер вообще ко всем данным, которые получены от непроверенного пользователя. Почему это следует делать, можно почитать в гугле по запросу что такое XSS уязвимость

    В начало раздела Наверх


    Регулярные выражения.


    Что такое регулярные выражения?
    Это замечательный инструмент для работы с текстом.
    Такой же революционный, по сравнению с обычными строковыми функциями, как БД - по сравнению с текстовыми файлами. Это специальный язык для работы с текстом. Причём подчас одна строчка с использованием регулярных выражений может заменить страницу другую обычного кода!

    С помощью регулярных выражений можно эффективно искать фрагменты текста любой сложности, заменять одни вхождения на другие.
    Основа регулярного выражения - шаблон. С его помощью мы описываем формат нужного нам фрагмента текста, а затем либо проверяем, подходит ли текст под шаблон, либо выразаем одно или несколько вхождений шаблона, либо заменяем на какой-либо текст.

    В данном топике собраны все ссылки, которые помогут вам быстро освоить и эффективно использовать регулярные выражения.
    Во-первых, это прекрасные статьи для начинающих Сергея Колесниченко (Yukko)
    Регулярные выражения, Часть I
    Регулярные выражения, Часть II
    Материал изложен в лёгкой и доступной форме, снабжён примерами решения часто встречающихся задач. Служит для первичного знакомства с предметом.

    При практическом же использовании не обойтись без справочных материалов официальной документации.
    В PHP поддерживается два стандарта регулярных выражений - POSIX и PCRE.
    Первый считается более устаревшим и медленным, вы не найдёте по нему хороших примеров и даже документация не переведена на русский.
    По PCRE (функции preg_*) документация очень солидная.
    Во-первых, это подробнейшее изложение синтаксиса и пояснение значения всех спецсимволов - Синтаксис регулярных выражений
    Во-вторых, - Общее описание, список функций и полезные комментарии
    В-третьих, пояснение довольно важного элемента - Модификаторов шаблонов
    Ну, и документация на все функции, разумеется. Основными из которых являются preg_match , preg_match_all и preg_replace с незаменимыми комментариями пользователей.
    Всё (кроме комментариев) - на русском языке.

    Нельзя, так же, обойти вниманием фундаментальный труд столпа российского PHP Дмитрия Бородина,
    Регулярные выражения в PHP (сравнение Perl и PHP)

    Так же можно скачать знаменитую книгу Дж.Фридла "Регулярные выражения" в формате DjVu.
    Книга написана с примерами на Perl, но разобраться там несложно.

    В изучении и работе с регулярными выражениями может пригодиться программа Regex Coach , с помощью которой можно наглядно увидеть, как отрабатывается тот или иной операнд шаблона.

    чтобы данный текст не выглядел сухой теорией, приведем пример решения наиболее распространённой задачи - "подсветка ссылок" или превращение http://phpfaq.ru в ссылку

    $text = preg_replace ( "#(https?|ftp)://\S+[^\s.,>)\];'\"!?]#" , '<a href="\\0">\\0</a>' , $text );

    Им можно пользоваться, не понимая ни слова. А узнать, что означают эти закорючки, можно из ссылок выше Smile happy

    В начало раздела Наверх


    Документация по PHP. Мануал по PHP, книги.

    Документация на русском языке:
    Документація на англійській мові.
    Книги, журнали
    Документация и книги по MySQL
    Коментарі

    Документація російською мовою:
    Перевод официальной документации - http://www.php.net/manual/ru/
    Далеко не все еще переведено, но, тем не менее, эту ссылку можно рекомендовать, как замену официального англоязычного мана. Что переведено - будет по-русски. Не переведенное же будет точной копией английского.
    Скачать полную версию документации в различных форматах (в том числе - в очень удобном .chm) можно здесь: http://www.php.net/download-docs.php
    Переклад робиться руками добровольців, яких, як завжди, не вистачає. Якщо ви хочете допомогти цієї благородної справи і відчуваєте себе в силах, то пишіть (in English please), в лист розсилки за адресою [email protected], а так само за цією адресою з вдячністю будуть прийняті повідомлення про неточності в перекладі.

    Удобно: Быстрый доступ к описанию функции из онлайн документации можно получить введя в браузере адрес www.php.net/имя_функции

    Существует полный русский перевод документации от версии 4.2 от некоего Александра Пирамидина.
    Перевод машинный, но тем не менее - вполне читабельный. Жахливі застарів.
    користуватися цим перекладом можна тільки в самому КРАЙНЬОМУ разі!
    Только если нужный вам участок официальной документации не переведен, а в английском, даже с переводчиком - ни в зуб ногой.
    Онлайн, на PHPClub-e, без баннеров: http://phpclub.ru/manrus/
    chm-версия: http://web.php.net.ua/download?what=php4

    Документация на английском языке.
    Самая актуальная и правильная:
    Онлайн, з коментарями користувачів: http://www.php.net/manual/en/
    Дуже зручно мати всю документацію в одному файлі з швидким пошуком. Такая документация есть, в формате windows help (.chm):
    http://www.php.net/distributions/manual/php_manual_en.chm
    Розширений варіант документації в форматі .chm, з дуже цінними коментарями користувачів постійно оновлюється на цьому сайті
    http://weblabor.hu/php-doc-chm

    Книги, журналы

    PHP. Сборник рецептов. Пeревод самой лучшей книги по PHP.

    Уникальная книга. Собрание конкретных ответов на конкретные вопросы. Як дату скласти, відняти, відформатувати. Как со строками работать, с файлами, с БД. Якщо порівнювати книги за кількістю води в них, то це буде пустеля Сахара. Книга разбита на 20 глав, каждая глава состоит из разделов вида: постановка проблемы - решение - объяснение.
    Немного устарела, в плане того, в чем пхп сильно ушел вперед - XML, обработка ошибок. Однако в базовых основах языка остается непревзойдённой. Може використовуватися як у вигляді довідника для вирішення конкретних проблем, так і у вигляді підручника.


    Котеров Д., Костарев А., "PHP5 в подлиннике".

    Второе издание знаменитой книги Д.Котерова. Не имеет ничего общего с первым. Это не переработанная, это совершенно новая книга. Уникальна тем, что подходит как начинающему, так и профессионалу - в ней изложены все аспекты программирования на PHP. В отличие от всех прочих скороспелок по "PHP5", вышедших ДО выхода самой пятой версии, книга действительно написана на материале релиза 5 версии.

    Якщо зовсім немає грошей на книги, то можна почитати дуже старий підручник Д.Гілмора "PHP - навчальний курс": http://phpfaq.ru/txt/gilmor

    Первый русскоязычный онлайн журнал по PHP - PHPinside.RU

    Сборник черезвычайно полезных сведений от авторa РНР: www.lerdorf.com/tips.pdf (на английском языке)

    Документація і книги по MySQL
    Самую свежую версию официальной документации для своей версии MySQL можно посмотреть на сайте http://dev.mysql.com/doc/
    Раньше там был и русский перевод к версии 4.0, но поскольку он устарел и не поддерживается, то его с сайта убрали. Посмотреть его по можно по адресу http://www.mysql.ru/docs/man
    Книга М.Грабера Введення в SQL http://www.mysql.ru/docs/gruber/



    П.Дюбуа. MySQL. Сборник рецептов
    Книга из суперсерии "cookbook" (книга рецептов) от автора "Библии MySQL" Поля Дюбуа. книга, необходимая каждому разработчику, использующему MySQL. Собрание ответов на любые практические вопросы. По ссылке можно ознакомиться с одной из глав книги.


    П.Дюбуа. MySQL. 2-е издание
    . Второе издание "Библии MySQL", на которой училось не одно поколение девелоперов и администраторов. Объясняет секреты MySQL от "а" до "я", но при этом очень простым и понятным языком.



    В начало раздела Наверх


    Ничего не работает! Что делать??? Поиск ошибок и отладка.

    Быстрые рекомендации.
    Вступ. Очень важное.
    Сообщения об ошибках PHP.
    Отладка и поиск ошибок в своем алгоритме.
    Пример отладки.
    Самое важное - знать, что ты хочешь получить.
    Висновок.
    Коментарі

    Швидкі рекомендації.
    1. Переконайтеся, що ви бачите повідомлення про помилки, якщо вони виникають.
    Для цього треба додати в початок скрипта 2 рядки
    ini_set ( 'display_errors' , 1 );
    error_reporting ( E_ALL );

    Хоча в деяких випадках це все одно не допоможе. Тоді дивіться помилки в логах веб-сервера.
    Ще можна додати в файл .htaccess рядок
    php_flag display_errors 1
    Обязательно убрать всех собак (@) из кода!
    Якщо апач видає помилку 500 - значить треба дивитися текст помилки в балці помилок веб-сервера.

    2. При проблемах с MySQL (supplied argument is not a valid MySQL result resource) под строкой, где произошла ошибка, обязательно надо вывести на экран mysql_error() и сам запрос - для визуального контроля и копирования на форум. повторюю - вивести треба ЗАПИТ! А чи не PHP-код, який його формує.

    3. При роботі з зображеннями, щоб побачити повідомлення про помилку, обов'язково треба здогадатися відключити висновок заголовка, говорить браузеру, що далі йде картинка.
    І, природно, звертатися до скрипту безпосередньо, а не через тег <img>!

    4. При проблемах у аплоаду в першу чергу дивіться масив $ _FILES ( print_r ( $_FILES ); print_r ( $_FILES ); ). Описания ошибок из $_FILES['filename']['error'] есть в мануале .

    5. При проблемах во взаимодействии сервера и клиента (куки, сессии, запросы)- в обязательном порядке смотреть обмен HTTP заголовками

    6. І НАЙВАЖЛИВІШЕ: запускаючи скрипт, дивіться не те, що показує браузер, а ВИХІДНИЙ HTML код! .

    Отримавши повідомлення про помилку, ви можете його прочитати і виправити.
    Якщо не впоралися - пишіть на форум. При цьому копіює повідомлення про помилку, і копіює невеликий - 3-5 рядків - шматок коду, на який вказує помилка. Повторюю - копіює! ніякої відсебеньок!

    Якщо ви все одно не знайшли помилку - читайте далі:

    Вступ. Дуже важливе.
    Ти написав програму, а вона не працює.
    Варіантів ти бачиш небагато - або сидіти і намагатися розумовою зусиллям виявити помилку, в сотий раз переглядаючи код, або піти на форум і попросити, щоб там тобі знайшли помилку.
    Найцікавіше, що є третій, в сто раз краще перших двох.
    Цей спосіб називається "Налагодження програми". По-англійськи - debug.
    Полягає він у тому, щоб змусити програму саму показати, де в ній помилка.
    Це мало того, що вийде швидше, ніж питати на стороні - так часто це єдиний спосіб вирішити проблему. Єдиний.
    Я тебе сейчас открою страшный секрет. У світі НІ програмістів, які пишуть код, як художники на Арбаті - сіл, наваял, віддав. Немає. І не буде.
    Процес написання програми - циклічний: Написав шматок коду - подивився, як працює. Якщо не працює - шукаємо помилки. Працює - пишемо далі.
    Тільки так. Інших варіантів немає.
    Більш того. У більшості випадків абсолютно марно вивалювати на форум свій код, і питати - "У чому помилка?" . На форумі не сидять чарівники упереміш з телепатами. І ворожок з віщунами - теж немає. Тому відгадувати, в чому, теоретично, може бути помилка, ніхто не буде. Помилку знайти може тільки господар програми. На своєму сервері. Зі своїми настройками і друкарськими помилками. Тому локалізувати помилку - знайти місце, де вона відбувається, визначити тип помилки - можна тільки самостійно. А ось виправити її на форумі допоможуть. Якщо не вийде самому.

    Ті, хто приходить до веб-програмування від дизайну, або від ігор, або знічев'я, просто не знають цієї страшної таємниці: Основний час програміста потрібно не на написання коду. Основний час програміста йде на пошук помилок і налагодження. Це не жарт. Це правда. І якщо ви вирішили зайнятися програмуванням, то вам доведеться шукати помилки точно так же, як це роблять всі інші.
    На жаль, дуже багато людей приходить до PHP взагалі без досвіду програмування і, як наслідок - ніколи не чули про налагодження.
    А це і є найголовніше в програмуванні - вміння шукати помилки.
    І ми з тобою зараз будемо вчитися їх шукати.

    Програма не працює. Що можна зробити в цьому випадку?

    Повідомлення про помилки PHP.
    Самий твій великий помічник у справі налагодження - це сам PHP. При виникненні будь-яких проблем він повідомить тобі про них. Тобто, в першу чергу ти повинен переконатися в тому, що якщо повідомлення про помилку є - ти його побачиш.

    Пояснення: Дуже багато людей не розуміють, що таке повідомлення про помилку. В основному ці люди діляться на дві категорії. Перша вважає, що повідомлення про помилки - це примха розробників мови, докучливий сервіс, зроблений для того, щоб програмісту було не нудно. І позбавлятися від повідомлень треба будь-якими доступними способами. Друга категорія не виросла зі шкільного віку і сприймає не зміст помилки, а лише сам факт. Приймають пхп за стервозного завуча, який лає не по справі, а абстрактно, за те, що телепень. Тобто, факт появи помилки викликає у цих людей тільки негативні емоції, а в сенс вони навіть не намагаються вчитуватися.

    Це жахливі помилки. Повідомлення про помилки - це ДОПОМОГА! Це величезна допомога програмісту. Як їй скористатися, ми розглянемо нижче.

    Навіть самий робочий код, якому ти на 100% довіряєш, і який на сусідній машині працює, як годинник, може видавати повідомлення про помилку. Причин тому може бути безліч. Це і такий поширений випадок, як відсутність прав доступу до файлів, і такі екзотичні, як заборона провайдером виконання найпоширеніших і нешкідливих функцій.

    Твоя задача - забезпечити інтерпретатора можливість повідомити тобі про помилку при виникненні оной.
    Ти повинен бути впевнений, що якщо виконання програми викликає помилку, то ти цю помилку побачиш.
    Що інтерпретатора не заборонено повідомляти тобі про помилку.
    Що ти зробив запит всі варіанти виведення повідомлення про помилку.

    По-перше, треба з'ясувати, виводяться помилки на екран або пишуться в лог. Зазвичай, домашній, або тестовий сервер налаштовується так, щоб помилки виводилися на екран. Робочий же сервер, з сайтом в публічному доступі ОБОВ'ЯЗКОВО повинен бути налаштований так, щоб помилки не виводилися на екран (оскільки відвідувачеві вони все одно нічого не скажуть, а програміст їх не побачить), а писалися в лог, де програміст їх побачить.
    Якщо ти не впевнений, і не знаєш, де подивитися, а помилку знайти треба терміново, то напиши на самому початку скрипта два рядки
    ini_set ( 'display_errors' , 1 );
    error_reporting ( E_ALL ^ E_NOTICE );

    Ці два рядки змусять виводити повідомлення усіх критичних помилок на екран.
    Якщо ніяких помилок не відбудеться з треба написати
    error_reporting ( E_ALL );
    Це дуже сильно допоможе, показавши неіснуючі змінні та інші дрібні помилки, які зазвичай ігноруються, але від яких може залежати працездатність програми.
    ВАЖЛИВО! У разі помилки синтаксису, з очевидних причин, установка за допомогою ini_set не спрацює.
    Тому краще на неї не сподіватися, а щось виправити в php.ini (або в .haccess додати рядок php_flag display_errors 1 ), або шукати помилку в балці.

    По-друге, переконайся, що в коді відсутні символи '@' перед іменами функцій. Цей забороняє висновок повідомлення про помилку. Отакої! Ти помилку шукаєш-іщещь, а сам же своїй програмі рот заткнув.

    Якщо ти впевнений, що помилка є, але на екран вона все одно не виводиться - знайди лог помилок веб-сервера. Зазвичай, це файл з назвою error_log. Де він знаходиться - треба подивитися в документації або запитати в службі підтримки провайдера.

    При возникновении проблем с функциями mysql (supplied argument is not a valid MySQL result resource) под строкой, где произошла ошибка, обязательно надо вывести на экран mysql_error() и сам запрос - для визуального контроля и копирования на форум.

    При роботі з зображеннями, щоб побачити повідомлення про помилку, обов'язково треба здогадатися відключити висновок заголовка, говорить браузеру, що далі йде картинка.
    При аплоаду в першу чергу дивіться масив $ _FILES.
    При проблемах во взаимодействии сервера и клиента - в обязательном порядке смотреть обмен HTTP заголовками
    І завжди дивіться не те, що показує браузер, а ВИХІДНИЙ HTML код!

    Припустимо, повідомлення про помилку з'являється, і ти його отримав. Що робити далі? Дуже просто - прочитати і виправити. Якщо не вистачає знання англійської мови, то варто або скористатися перекладачем, або взяти значущу частину цих слів і запросити Google. 90% ймовірності, що хтось з такою помилкою вже стикався, і ти тут же прочитаєш відповідь.
    Якщо ж не знайшов, то запитай в форумі, точно скопіювавши невеликий (3-5 рядків) шматок коду, в якому сталася помилка, точно вказавши рядок, про яку йдеться в повідомленні про помилку, а так само - найголовніше! - Саме повідомлення про помилку.
    Погодься, що з такою інформацією тобі на форумі допоможуть набагато швидше і якісніше?

    Налагодження і пошук помилок в своєму алгоритмі.
    Але буває так, що програма не викликає помилок, але все одно не працює, або працює не так, як треба.
    Тут уже винен або алгоритм або якісь зовнішні чинники.
    Однак і тут можна знайти місце, де відбувається помилка.
    Але тільки за однієї умови.
    що ти чітко уявляєш, що робить твоя програма, кожна функція, кожен рядок в ній. Тому, що якщо ти уявляєш, то можеш передбачити, які значення повинні мати змінні на кожному етапі виконання.
    А далі все ДУЖЕ просто!
    По-перше, треба розділити програму на логічні блоки.
    Припустимо, скрипт виводить форму, отримує її, і записує дані в базу. ТРИ кроку! І в будь-якому з них може бути помилка, яка призводить до того, що дані в базу не записуються.
    Треба проконтролювати на кожній з ділянок - чи змінні мають те значення, яке очікується.
    Програма адже працює зі змінними.
    Як перевірити?
    Виводити всі використовувані змінні на екран! І візуально контролювати їх вміст.
    Всего-то лишь написать проблемных местах var_dump($var) и выяснится, что переменная-то пустая!
    І вже можеш піти на форум не з питанням "у мене ось код на 100 рядків, де помилка?", А "я написав функцію, але чомусь, коли звертаюся в ній до змінних, вони всі порожні". або "з форми не передаються змінні".
    Між цими двома способами завдання питань - прірва.
    Перший не може тобі допомогти ніяк. Ти, власне, і сам не знаєш, що у тебе за проблема. А при другому ти вже знаєш проблему, і, якщо сам не впорався з її рішенням, то можеш поставити на форумі конкретне питання.

    Ще дуже допоможе уникнути помилок в програмі виставлення error_reporting в E_ALL з самого початку роботи скрипта.
    Якщо при вилові критичних помилок повідомлення про потенційних помилки можуть нам перешкодити побачити головну, то при розробці нам бажано бачити все - і потенційні в тому числі. Скажімо, при E_ALL, при зверненні до неіснуючої змінної, PHP видасть попередження. Тобто, тобі не доведеться самому виводити змінну, щоб з'ясувати, що ти не присвоїв їй ніякого значення - РНР тебе сам попередить.

    Приклад налагодження.
    З html форми передаються чекбокси з іменами c_1, c_1, c_3 ... c_10
    В скрипті ми намагаємося в циклі вивести
    for ( $i = 1 , $i < 11 , $i ++) {
    echo
    $_POST [ 'с_$i' ];
    }

    скрипт нічого не виводить.
    Починаємо налагоджувати.
    Спочатку дивимося в вихідний код html сторінки. чи відповідає вона стандартам?
    Припустимо, відповідає. Значить, проблема не в формі.
    Далі, перевіряємо в скрипті - а чи є така змінна, до якої ми звертаємося - масив $ _POST?
    пишемо

    echo '<pre>' ;
    var_dump ( $_POST );

    Переконуємося в тому, що масив є і всі елементи на місці. Значить, проблема не в передачі.
    Значить, ми якось неправильно звертаємося до масиву.
    звертаємося ми до нього так: $ _POST [ 'з_ $ i']
    Треба перевірити - а у що перетворюється 'з_ $ i'?
    робимо echo 'з_ $ i'; і бачимо ... зовсім не те, що очікували побачити.
    І ось тепер вже йдемо або читати документацію про рядки в пхп (що переважно), або - на форум, з питанням "чому у мене змінна не замістити своїм значенням". Який питання буде набагато краще звучати, ніж "у мене форма не працює".
    Зрозуміло?

    Слід розуміти, що тут наведено приклад, Нереальний. Показано алгоритм дій.
    У реальності, при error_reporting (E_ALL); PHP відразу ж показав би, що індекс масиву у вас неправильний.

    Найважливіше - знати, що ти хочеш отримати.
    Приблизно половина питань на форумах викликана тим, що людина робить щось ... НЕ ЗНАЮЧИ, що саме!
    Найгеніальніший питання всіх часів і народів: "у мене база з'їла все переклади рядків з Текстар".
    Людина просто не дав собі працю подивитися, як буде виглядати HTML, який він хоче отримати, і вирішив, що переведення рядків з'їла база.
    І так у всьому.
    Невизнаний геній будує складний SQL запит, а коли його запитують, як запит повинен виглядати - він тільки кліпає очима. ЗАВЖДИ СПОЧАТКУ складіть запит руками і виконайте в консолі або phpmyadmin! А після того, як отримали потрібний запит і налагодили - ласкаво просимо, складайте його на пхп.
    Робота з віддаленим хостом через сокет по протоколу HTTP - те ж саме! Спочатку навчіться виконувати всі команди руками, подивіться відповіді сервера очима, а потім моделюйте цей діалог в пхп.
    Работа с яваскриптом по тому же методу описана в факе "на танке"

    Запам'ятайте - на пхп ви працюєте тільки з рядками! HTML сторінка, яку ви створюєте скриптом - це для пхп всього лише набір рядків! Йому без різниці, що в цьому наборі - теги img, script або frame. Пхп за вас не зробить переклади рядків, що не намалює яваскрипт. Якщо ви не знаєте Яваскрипт - то не намагайтеся створювати програму на ньому за допомогою PHP.
    Відкриваючи з'єднання з віддаленим хостом, ви посилаєте рядок в сокет, ви отримуєте рядок з сокета. Пхп нічого не розуміє в цих рядках і за вас діалог вести не буде! Це ВИ повинні чітко розуміти, що ви хочете послати в сокет, і що отримати! Тому візьміть програму telnet, з'єднаєтеся з потрібним хостом і пробуйте спочатку САМІ зробити те, що хочете змусити зробити пхп.
    Якщо у вас не працює скрипт з сокетами - бігом в телнет дивитися, що відбувається!
    SQL запит - це СТРОКА. Ви повинні собі чітко уявляти, який запит вийде в результаті вашого хитромудрого пхп-коду! Сервер БД не розуміє конструкцій intval, date, mktime і так далі! Це все пхп-код. Результатом якого буде рядок коректного SQL запиту. перш, ніж писати пхп код, ви повинні ЧІТКО СЕБЕ ПРЕДСТАВЛЯТИ, ЯК ПОВИНЕН МАТИ SQL ЗАПИТ В РЕЗУЛЬТАТІ!
    Якщо у вас не виконується SQL запит 0 виводите його на екран і дивіться - що нагородили своїм скриптом!

    Висновок.
    Налагодження - головне заняття програміста.
    Налагодження - єдиний і найпотужніший спосіб знайти помилку в програмі.
    Налагодження складається з двох основних компонентів:
    1. Максимально спрощувати приклад. Якщо у вас не працює програма, яка малює форму, отримує дані, записує дані форми в базу і виводить їх знову, то розбийте програму на складові і виконуйте по черзі.
    Якщо у вас не працює складна підпрограма визначення працездатності кук - напишіть спочатку тест в два рядки щоб переконатися, що ви хоча б можете виставляти і читати куку.
    2. Висновок налагоджувальної інформації.
    Перевіряйте значення КОЖНІЙ змінної! Кожного значення, що повертається функцією!
    Чи не працює локейшен? Виведіть його на екран і скопіюйте в браузер!
    У файл записується порожній рядок? перевіряйте складові цього рядка на кожному етапі її створення і виводите на екран!
    Переконалися, що на екран виводиться? Тренуйтеся писати в файл, на тестовій рядку! Забитої прямо в скрипт! Зменшуйте кількість невідомих!
    І завжди дивіться не те, що показує браузер, а ВИХІДНИЙ HTML код!

    Сподіваюся, що я зміг хоча б трохи пояснити принципи цього заняття.
    Вдалою налагодження.


    В начало раздела Наверх


    Решение проблемы "Cannot add header information - headers already sent"


    Ошибку эту исправить несложно.
    Часто такое же сообщение появляется при старте сессий, в немного другой формулировке:
    Warning: Cannot send session cookie - headers already sent
    Warning: Cannot send session cache limiter - headers already sent

    Для начала узнаем, как вообще общается броузер с сервером. Происходит это по специальному протоколу HTTP . К примеру, когда ты набраешь адрес, или нажимаешь на ссылку, броузер посылает HTTP запрос серверу. Сервер отвечает. Первыми в ответе ВСЕГДА идут HTTP заголовки. Хоть один. И только потом уже сервер посылает, а броузер принимает, текст, или картинку, или файл - в общем, что было запрошено. Cобственно, из-за этого правила - сначала заголовок, а потом информация, и происходит данная ошибка. РНР, для твоего удобства, посылает заголовки автоматически, как только скрипт начинает выдавать бровзеру информацию. Соответственно, если хоть один пробел был уже передан пользователю, заголовки уже ушли, и снова их послать уже никак не можно. А, как ты уже, наверное, догадался, команды header() , setcookie , session_start() , посылают HTTP заголовки.

    Разберем теперь это предупреждение.
    Warning: Cannot add header information - headers already sent by (output started at /www/script.php:5) on line 20

    Cannot add header information - headers already sent . Все ясно написано. "Не могу послать заголовок, поезд уже ушел" - пишет нам РНР. Дальше РНР сообщает, в каком скрипте и в какой его строке ( output started at /www/script.php:5 ) произошел вывод информации, вызвавший автоматическую посылку заголовков. Очень легко найти и исправить. Может быть, там html теги, может быть, echo, а может и просто незамеченая пустая строка или пробел перед первым тегом <? .
    Очень часто такую ошибку вызывает файл, подключаемый через include , в котором либо есть какой-то вывод, либо пустая строка после закрывающего PHP тега - обнаружить ее очень трудно.

    Для решения этой проблемы нужно функцию header () (или session_start (), setcookie ) и всю логику, которая ее вызывает, поместить ДО любого вывода в броузер. Просто перенести повыше в скрипте.
    Ведь вы всё равно перенаправляете браузер. То есть, никакой текст всё равно не будет выведен! Значит, и выводить что-то одновременно с заголовком Location нет смысла. Правильно планируйте структуру своего скрипта: блок, который обрабатывает POST, не должен ничего выводить в браузер.

    Иногда вы проверили ВСЁ - нигде ничего нет. Смените редактор. Посмотрите свой файл в другой программе. К примеру, Windows Блокнот при использовании кодировки Unicode добавляет в начало вашего файла служебный символ Byte Order Mark , никак при этом не ставя вас в известность. Откройте скрипт в другом редакторе и удалите посторонние символы. И смените Блокнот на другой редактор.

    Многочисленные вопросы на форуме заставляют меня сделать здесь важное замечание:
    Эта ошибка появляется не от того, что у вас в скрипте "что то написано выше". А от того, что РНР выводит что-то в браузер. Это не обязательно код. Это может быть сообщение об ошибке . может быть пробел или хтмл тег. Да-да. Для самых талантливых: речь идет о любом символе, отправленном в браузер, а не только о тех, которые браузер отображает неискушенному пользователю. У HTML страниц есть исходный текст. И именно он является результатом работы PHP скрипта, а не красивые буковки с картиночками, как думает очень большое количество людей.

    В начало раздела Наверх


    Не передаются переменные! Проблема Undefined variable


    Во всех старых руководствах по РНР написано, что даные, полученные из формы, или переданные по ссылке, вот так: script.php?peremennaya=znachenie&variable=value
    автоматически становятся переменными PHP, $peremennaya и $variable
    Ця інформація застаріла.
    Дело в том, что в целях безопасности, начиная с версии 4.1, РНР настраивается по умолчанию так, чтобы переданные значения не назначались переменным.
    Отримати ж передане значення можна звернувшись до відповідного масиву.
    наприклад:
    Якщо передаємо методом GET, звернувшись до скрипту за посиланням виду
    script.php?var=value
    або відправивши форму, вказавши в ній method = "GET",
    то всі змінні містяться в масиві $_GET .
    echo $_GET [ 'var' ] ; надрукує " value ".
    Если получаем данные из формы, отправленной методом POST, то все поля этой формы содержатся в массиве $_POST . Припустимо, в формі був елемент
    <input type="hidden" name="var" value="1"> ,
    то в скрипте, который указан в action формы, можно написать echo $_POST [ 'var' ]; и будет выведена 1.

    Поэтому, если вы уверены, что переменная есть, но вы не можете ее найти - ищите ее в суперглобальных массивах.
    Подробнее о них можно почитать на русском языке в официальной документации
    http://ru2.php.net/variables.predefined

    То же касается и серверных переменных, таких, как $REMOTE_ADDR , $PHP_SELF . Получить их можно, обратившись к массивам $_SERVER , $_ENV или функцией getenv -
    getenv ( 'HTTP_REFERER' )
    ;

    Змінні, зареєстровані в сесії, слід шукати в масиві $_SESSION .
    Дані cookie містяться в масиві $ _COOKIE, відомості про закачані файлах - в $ _FILES
    Переменные окружения - в $_ENV, а так же, существует массив $_REQUEST, в котором собраны данные из GET, POST и cookie.

    Важливо! Весьма в поисках переменных помогает одна их главных функций PHP - phpinfo ()
    ее следует применять всякий раз, когда вы "потеряли" переменную, вызовите phpinfo ( 32 ); в скрипте, в котором не работает авторизация, в скрипте, который принимает файл при аплоаде - и все найдется!


    Все то же самое можно прочесть и в документации: http://www.php.net/manual/ru/security.globals.php

    В начало раздела Наверх


    Различие между абсолютными и относительными путями. В файловой системе и на сайте.


    твой сайт существует в как бы в двух измерениях.
    Реальном и виртуальном.

    Для всех посетителей - это виртуальный веб-сервер. Который отличается, в числе прочего, тем, что на нем НЕ СУЩЕСТВУЕТ ФАЙЛОВ. если ты пишешь http://site.ru/file.html - это не файл. Это URI, виртуальный адрес. Никакого файла с именем file.html на сервере может вообще не быть. Это все виртуальные адреса, а не файлы.
    И браузер работает именно с адресами.

    Для разработчика же сайт - это программа, выполняющаяся на совершенно конкретном реальном компьютере. С совершенно конкретным жестким диском, каталогами и файлами. И скрипт, работая со своими данными, подгружая другие скрипты, работает именно с реальными ФАЙЛАМИ, на физическом ДИСКЕ.

    Вот в этом различии и кроются трудности, с которыми часто сталкиваются новички.
    Теряют файлы, путают ссылки с файлами, обращаются к локальным файлам по протоколу HTTP, или инклюдят файлы от корня веб-сервера.

    А всего-то надо четко понимать две вещи:
    1. Различать корень веб-сервера, как его видит браузер, и корень файловой системы на диске.
    2. Отличие относительных путей от абсолютных.

    Начнем со второго.
    Це дуже просто. Если путь указывается от корня системы, то это путь абсолютный. Это как почтовый адрес в реальной жизни - откуда бы ты не шел, но по точному адресу ты всегда точно найдешь нужное место.
    примеры абсолютных путей:
    /var/www/site/forum/index.php
    /img/frame.gif
    с:\windows\command.com

    В юникс-системах и на веб сайтах корень обозначается косой чертой - "/".
    Це важливо. Это не просто палочка, а самостоятельный АДРЕС, путь.
    В адресе http://www.site.ru/ последняя косая черта - не для красоты! Она обозначает вполне конкретный адрес - начало сайта.
    На диске в юникс системах так же можно набрать "cd /" и ты попадешь в корневой каталог.
    В виндоус системах файловая система разбивается по дискам, поэтому, в абсолютном адресе надо указывать имя диска. Абсолютного корня всей файловой системы в виндоус нет, у каждого диска - свой. Например, C:\ E:\
    поэтому, даже если путь в виндоус начинается с косой черты, то это не абсолютный путь, а относительный. Относительно текущего диска. А абсолютный начинается с буквы.

    Если в начале пути корень не указать, то этот путь будет относительным, и он достаивается от текущего положения. В реальной жизни это напоминает дорогу к винному магазину - "два квартала налево и там все время прямо". Дойти по такому пути можно только из конкретной точки. Из другой ты попадешь уже в совсем другое место.
    Самый простой пример относительного пути - это просто имя файла.
    Если файл находится в том же каталоге, с которым работает программа - она его найдет, добавив текущий путь к имени файла.
    примеры относительных путей:
    file.php (фал лежит в той же папке)
    ./file.php (фал лежит в той же папке. такая запись иногда требуется в некоторых юникс системах)
    images/picture.jpg (файл лежит в капке images, которая находится в текущей)
    ../file.php (файл лежит в папке, которая расположена на один уровень выше от текущей)
    ../../file.php (файл лежит в папке, которая расположена на два уровня выше от текущей)

    И операционная система, и браузер, встретив относительный путь, достраивают его до абсолютного. но каждый - по-своему.

    Теперь перейдём к первому пункту.
    Различие корня веб-сервера, как его видит браузер, и корень файловой системы на диске.
    В общем-то, из предыдущих объяснений уже все должно быть понятно.
    На диске путь к файлу скрипта может быть таким:
    /var/www/site/forum/index.php
    В то же время, виртуальный адрес этого скрипта при просмотре через браузер, будет:
    http://www.site.ru/forum/index.php
    На этом примере легко увидеть, где пересекаются два измерения: у этих двух адресов есть общая часть - /forum/index.php - и она-то и служит причиной путаницы.
    Для браузера это самый полный путь, который только может быть. Он начинается от корня сайта.
    Для скрипта же, исполняющегося на сервере - это всего лишь ЧАСТЬ пути.
    для скрипта путь /forum/index.php окажется несуществующим - в корне диска нет каталога forum!
    чтобы получить полный путь для того, что на сайте выглядит, как /forum/index.php , надо приставить слева к нему путь к папке, которая считается корневаой для всего веб сервера.
    в нашем примере - это
    /var/www/site
    Этот путь задается в кофигурации веб-сервера и именно он содержится в системной переменной PHP $_SERVER [ 'DOCUMENT_ROOT' ]

    В виртуальном же сервере - том, который видит пользователь - наоборот, нет никакого диска. Есть корень сайта. То есть, для того, чтобы любая ссылка гарантированно работала, независимо от того, из какого места сайта она вызывается, она должна быть абсолютной.
    Если у вас на сайте есть, допустим, два раздела:
    http://www.site.ru/about/info.php
    і
    http://www.site.ru/job/vacancy.php
    то, если в файле info.php сделать ссылку просто на vacancy.php , то браузер ее не найдет - он будет искать адрес http://www.site.ru/about/vacancy.php , достраивая путь от текущего каталога.
    Поэтому надо писать полный путь от корня сайта - /job/vacancy.php
    Всё это касается, естественно, не только тегов <a> но и <img> и любых других, где используются ссылки на другие файлы.

    Ссылки на локальные адреса следует писать без указания протокола и домена - только путь от корня сайта - /job/vacancy.php . Ссылки же на другие сайты следует писать полностью - http://www.site1.ru/job/vacancy.php .


    PHP предоставляет множество средств для работы с файлами, каталогами и URL-ами.

    Во-первых, это многочисленные предопределённые переменные, которые описаны в документации и значения которых в своём скрипте пможно посмотрев с помощью phpinfo() :

    Константа __FILE__ содержит имя текущего исполняемого файла.
    В отличие от PHP_SELF она содержит имя файла, исполяющегося в данный момент.
    очень полезной представляется конструкция dirname ( __FILE__ ) , на которую желательно заменить все вызовы файлов, лежащих в том же каталоге, что и вызывающий скрипт. наприклад:
    require dirname ( __FILE__ ). "/init.php"
    функция dirname() , наряду с basename() является одними из наиболее употребительных для работы с файлами и каталогами.




    В начало раздела Наверх


    Как разбить вывод из mysql постранично


    Как сделать постраничный вывод из mysql "как в яндексе"?
    по 10 записей на страницу, внизу - ссылки на остальные страницы?

    Сначала научимся получать из базы нужные записи.
    Их получение в mysql обеспечивается оператором LIMIT, который вызывается с двумя параметрами - с какой записи начинать, и сколько выводить (внимание! не по какую, а сколько!)
    SELECT * FROM table LIMIT 0,10
    этот запрос вернет записи с первой по 10, поскольку нумерация начинается с 0
    соответственно, запрос для третьей страницы будет выглядеть, как
    SELECT * FROM table LIMIT 20,10
    получается, что нам всего лишь надо передать в скрипт число, которое потом подставить в запрос.
    Этим будет заниматься код, который выводит ссылки на страницы.
    Естественно, в цикле.
    Для цикла нам понадобится количество записей, которое возвращает запрос без лимита.
    Это число можно получить двумя путями. Либо отдельным запросом, в котором отсутствует оператор LIMIT, а вместо перечисления полей после оператора SELECT запрашивается только count(*):
    $q = "SELECT count(*) FROM table" ;
    $res = mysql_query ( $q );
    $row = mysql_fetch_row ( $res );
    $total_rows = $row [ 0 ];


    Либо, если версия mysql больше 4.0, то общее количество строк можно запросить в том же запросе. См. документацию mysql по функции FOUND_ROWS()
    Однако, первый сособ представляется более удобным, хотя и немного более медленным.

    Для тех, кто предпочитает думать самостоятельно, этой информации достаточно.
    Вывод ссылок - это простейшая арифметическая операция и никаких особенных знаний для нее не требуется.
    Для тех, же, кто ищет готовый код, продолжим.

    Для начала определим, сколько всего получится страниц. Для этого надо поделить общее число записей на количество оных на одной странице и округлить результат в большую сторону. Таким округлением занимается в пхп функция ceil()
    $num_pages = ceil ( $total_rows / $per_page );
    В этом выражении участвует переменная $per_page , в которую мы положим количество выводимых на странице записей.
    Ведь, если это количество изменится, мы же не хотим ползать по всему коду и исправлять цифры? проще сделать это один раз в начале скрипта при объявлении переменной. В запрос, вторым параметром LIMIT, подставлять нужно, конечно же, тоже ее.

    Ну, а дальше, собственно, вывод ссылок.
    for( $i = 1 ; $i <= $num_pages ; $i ++) {
    echo
    '<a href="' . $_SERVER [ 'PHP_SELF' ]. '?num=' . $i * $per_page . '">' . $i . "</a>\n" ;
    }

    в цикле от 1 до $num_pages выводим ссылку с параметром num, равным числу, которое надо передать в LIMIT, а в тексте ссылки пишем номер страницы, поскольку людям понятнее видеть номер страницы, а не записи. На код это не влияет, а людям приятно.

    Дальше пойдут украшения.
    Во-первых, некрасиво, что номер страницы не совпадает с тем, что видно в адресной строке. Эту проблему можно решить, передавая по ссылке номер страницы в человекопонятном формате, а в скрипте вычислять первый операнд для LIMIT.
    Во-вторых, мы явно захотим выделить текущую страницу, не оформляя ее ссылкой.
    В-третьих, мы захотим нумеровать записи

    что у нас в результате получилось?

    // количество записей, выводимых на странице
    $per_page = 10 ;
    // получаем номер страницы
    if (isset( $_GET [ 'page' ])) $page =( $_GET [ 'page' ]- 1 ); else $page = 0 ;
    // вычисляем первый оператор для LIMIT
    $start = abs ( $page * $per_page );
    // составляем запрос и выводим записи
    // переменную $start используем, как нумератор записей.
    $q = "SELECT * FROM `table` ORDER BY field LIMIT $start,$per_page" ;
    $res = mysql_query ( $q );
    while(
    $row = mysql_fetch_array ( $res )) {
    echo ++
    $start . ". " . $row [ 'field' ]. "<br>\n" ;
    }

    // дальше выводим ссылки на страницы:
    $q = "SELECT count(*) FROM `table`" ;
    $res = mysql_query ( $q );
    $row = mysql_fetch_row ( $res );
    $total_rows = $row [ 0 ];

    $num_pages = ceil ( $total_rows / $per_page );

    for(
    $i = 1 ; $i <= $num_pages ; $i ++) {
    if (
    $i - 1 == $page ) {
    echo
    $i . " " ;
    } else {
    echo
    '<a href="' . $_SERVER [ 'PHP_SELF' ]. '?page=' . $i . '">' . $i . "</a> " ;
    }
    }


    Разумеется, вышеприведённый код подходит только как учебное пособие. С его помощью становится понятным принцип, но в реальных условиях мы сразу же столкнемся, как минимум, с двумя проблемами:
    Во-первых, кроме переменной $page нашему крипту явно будут переданы и другие переменные, да и адрес может совсем не совпадать с именем скрипта. А мы это при формировании ссылок не учитываем.
    Во-вторых, нормальный современный сайт немыслим без шаблонов. И такая ужасная лапша из SQL запросов, PHP кода и HTML тегов никуда не годится.

    Займемся решением этих проблем.
    Первая решается просто:
    $uri = strtok ( $_SERVER [ 'REQUEST_URI' ], "?" ). "?" ;
    if (
    count ( $_GET )) foreach ( $_GET as $k => $v ) if ( $k != "page" ) $uri .= urlencode ( $k ). "=" . urlencode ( $v ). "&" ;

    и полученную переменную $uri подставляем в код вместо $_SERVER['PHP_SELF']

    Вторая - тоже несложно. Шаблонизаторов много, но мы воспользуемся самым универсальным - PHP.
    Что же у нас получилось? А получился у нас - рефакторинг ! Переделка старого кода в соответствии с требованиями современности, плюс мелкое причесывание:

    <?
    //определим фрагмент запроса, который отвечает за то, какие записи мы запрашиваем
    $from_where = "FROM table WHERE filter=1" ;
    // и получим общее количество записей
    $res = mysql_query ( "SELECT count(id) " . $from_where );
    $row = mysql_fetch_row ( $res );
    $total_rows = $row [ 0 ];

    //дальше получаем номер страницы и значение для лимита
    if (isset( $_GET [ 'page' ])) $CUR_PAGE =( $_GET [ 'page' ]); else $CUR_PAGE = 1 ;
    $start = abs (( $CUR_PAGE - 1 )* $per_page );

    //выполняем запрос и получаем данные для вывода
    $query = "SELECT * $from_where ORDER BY date DESC LIMIT $start,$per_page" ;
    $res = mysql_query ( $query );
    while (
    $row = mysql_fetch_array ( $res )) $DATA [++ $start ]= $row ;

    //определяем адрес страницы без переменной page
    $uri = strtok ( $_SERVER [ 'REQUEST_URI' ], "?" ). "?" ;
    if (
    count ( $_GET )) {
    foreach (
    $_GET as $k => $v ) {
    if (
    $k != "page" ) $uri .= urlencode ( $k ). "=" . urlencode ( $v ). "&" ;
    }
    }

    //узнаем общее количество страниц и заполняем массив со ссылками
    $num_pages = ceil ( $total_rows / $per_page );
    for(
    $i = 1 ; $i <= $num_pages ; $i ++) $PAGES [ $i ]= $uri . 'page=' . $i ;

    //а дальше выводим в шаблоне днные и навигацию:
    ?>
    Найдено сообщений: <b> <?=$total_rows?> </b><br><br>
    <? foreach ( $DATA as $i => $row ): ?>
    <?=$i?>
    . <a href="?id= <?=$row [ 'id' ] ?> "> <?=$row [ 'title' ]) ?> </a><br>
    <? endforeach ?>

    <br>
    Страницы:
    <? foreach ( $PAGES as $i => $link ): ?>
    <?
    if ( $i == $CUR_PAGE ): ?>
    <b> <?=$i?> </b>
    <? else: ?>
    <a href=" <?=$link?> "> <?=$i?> </a>
    <? endif ?>
    <? endforeach ?>



    В начало раздела Наверх


    Определение IP адреса

    Беллетристика
    теорія
    Практика.
    Примечания.
    Коментарі

    Беллетристика
    Один из самых дремучих вопросов в околопхпешном вебе - это определение IP адреса.
    Такого количества неправильного кода не написано, наверное, ни для какой другой операции.

    Каждый, кто в один прекрасный день узнаёт о существовании переменной HTTP_X_FORWARDED_FOR, тут же воображает себя мегагуру, и заменяет ей REMOTE_ADDR. Потом приходит знание о других переменных (X_REAL_IP, VIA, и ещё вагон и маленькая тележка), изобретаются многослойные мегаконструкции, изобретатели хвастаются друг перед другом их многоэтажностью и сравнивают свои творения с "кодом из PHPbb!".

    При этом спроси любого из них - "какой именно адрес они хотят определить?" - ни один не ответит: понимание основ функционирования сети TCP/IP среди пхп-программистов традиционно слабое.
    А вот стремление к нахождению Идеального и Единственно Правильного Решения - традиционно сильное.
    В результате вместо IP адреса в логи пишется не пойми что.

    К примеру, возьмем, казалось бы, простой вопрос "какой именно IP адрес (из цепочки хостов, через которые идет запрос от компьютера клиента к серверу) мы хотим записать в лог?". После того, как выяснилось, что большинство пхп-программистов затрудняются на него ответить, я и решил написать эту заметку.

    А это, между прочим, очень важный вопрос. Не ответив на него, наряду с вопросом " Зачем нам нужен IP адрес?", приступать к самому определению бессмысленно.
    При том, что большинству читателей этого текста вопросы покажутся бессмысленными.
    Ну что ж, попробуем разобраться.

    теорія
    Во-первых. Самые азы. Для тех, кто не знает.
    Все элементы массива $_SERVER, начинающиеся со слова "HTTP_" - это HTTP-заголовки.
    Как уже знают вдумчивые читатели фака на танке, HTTP заголовки присылает клиент. И прислать может любые.
    К примеру, заголовок X-All-Your-Base-Belongs-To-Us: Surrender!
    Или, как вы уже, наверное, догадались, заголовок X-Forwarded-For: admin durak
    Мне кажется, что записывать столь глубокомысленную строку вместо IP адреса - не самая лучшая идея.
    Как и вообще доверять любым переменным, начинающимся с HTTP. Это первое правило, которое надо запомнить с молоком матери: Любые элементы массива $_SERVER, начинающиеся с "HTTP_", можно использовать только в справочных целях! К примеру, HTTP_REFERER записываем, чтобы потом посмотреть. Но ни в коем случае не делаем на него Location.

    Во-вторых, определимся с тем, ДЛЯ ЧЕГО нам нужен IP адрес. Если мы хотим записать в лог, то пишем однозначно только REMOTE_ADDR. В этой переменной содержится реальный IP адрес реального хоста в интернете, который произвел соединение с нашим сервером. Единственный реальный адрес. Никаких других сервер не знает.
    Апач пишет в логи именно REMOTE_ADDR. Не надо считать авторов веб-сервера дурнее себя.

    Что значит - реальный IP адрес? А то и значит. Адрес хоста, который произвел соединение с нашим сервером. Этот адрес по определению может быть только один. Один, а не 5 по цепочке. Рассмотрим типичный пример:
    Есть пользовательский компьютер, который, который находится в офисной сети. IP компьютера 192.168.0.22
    Офисная сеть включена через роутер в сеть здания. IP роутера - 10.10.0.3
    Сеть здания, в свою очередь, подключена к интернету, через роутер. IP роутера - 77.88.22.11
    Пользователь заходит на сайт, через НТТР прокси. IP прокси - 212.121.0.8
    Так вот, сеть TCP/IP так устроена, что каждый следующий узел ничего не знает о предыдущих. Есть только пара хостов, которые соединяются друг с другом. В самих TCP/IP пакетах никакой информации о предыдущих хостах не предусмотрено.
    Поэтому, как это ни обидно, но реальным адресом мы можем считать только последний в цепочке - адрес HTTP прокси.

    Больше того. Ну допустим, узнали мы адрес компьютера пользователя (чем кичатся многие определители с помощью activeX и ява-апплетов). Этот адрес - 192.168.0.22. Он из приватной сети . Компьютеров с такими адресами в мире - миллионы. Найти компьютер по такому адресу - невозможно. Пользы от него - практически никакой. Практически, но не совсем. Чому? Слушаем дальше:

    Поскольку в протоколе HTTP текстовые заголовки, то в них можно добавить свой. Что некоторые хосты и делают. В те самые X-Forwarded-For, Via и прочие.
    Можем мы их использовать? Можем. Если правильно понимать - для чего.
    Для определения "реального IP адреса", как мы уже убедились - нельзя. А для чего же можно? Например - справки. просто записать, на всякий случай. АЛЕ! Только в том случае, если мы откажемся от дурацкой идеи найти один идеальный IP адрес. Если мы не будем писать вместо реального всякую лабуду, а будем записывать все похожее на IP адрес с реальным наряду, то почему нет?
    Итак, можно записать всё, похожее на IP адрес. Понадобится выявить злостного вредителя - возможно, какой-то интересный айпишник среди заголовков и проскочит.
    Для небольшого повышения надежности сессий - тоже можно. Писать в сессию не только реальный, но и все похожие. И все сверять. Хоть один не совпал - сессию рубим.
    И в других подобных случаях.
    Не забывая: реальный - отдельно, все похожее на IP адрес - отдельно.
    Не забывая: особо полагаться на все эти заголовки не стоит.

    Практика.
    Отже. Из всего вышеизложенного делается простой вывод.
    IP адрес в скрипте может быть только один. Лежит он в переменной REMOTE_ADDR.
    Следовательно, вожделенный код получания "идеального IP адреса" выглядит, как
    $ip = $_SERVER [ 'REMOTE_ADDR' ]
    Крапка.

    Далі. Если мы хотим воспользоваться "заголовками, похожими на IP адрес" (лучше всего, во избежание недоразумений, совсем не считать их адресами хостов, а HTTP заголовками особого формата. Тем более, что никакого стандарта на содержимое заголовков X-Forwarded-For, Via и прочих - нет. Там могут оказаться IP адреса чарез запятую, или доменные имена или не через запятую. Не говоря уже о подделках!), то нет смысла судорожно искать все имена заголовков, где может встретиться адрес. Проще искать сами адреса.
    Берем, пишем простой код, который в цикле перебирает массив $_SERVER, и регулярным выражением выцепляет все заголовки, в которых встречается подхдящая под шаблон IP адреса строка. Если встретилась, то весь заголовок - с именем и всем содержимым - добавляем в массив или в строку. Которая хранится отдельно от IP адреса, в текстовом виде.
    Соответственно, в нужном месте повторяем операцию, и сверяем. IP адрес с IP адресом, строку похожих заголовков - со строкой похожих заголовков.
    function get_all_ip () {
    $ip_pattern = "#(? :( ?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)#" ;
    $ret = "" ;
    foreach (
    $_SERVER as $k => $v ) {
    if (
    substr ( $k , 0 , 5 )== "HTTP_" AND preg_match ( $ip_pattern , $v )) $ret .= $k . ": " . $v . "\n" ;
    }
    return
    $ret ;
    }

    Такой вот, несложный код.
    Правда, нужда в нем, если задуматься, очень невелика. Разве что, для тех же сессий. А для справки, про запас... не проще ли писать вообще все HTTP заголовки, пришедшие в скрипт? И это поинформативнее будет, чем выцеплять какой-то один адрес из HTTP_X_REAL_IP.
    Да и для сессий следует применять с осторожностью - IP адрес может оказаться, к примеру, в реферере...

    Примечания.
    Недавно я выяснил удивительную вещь. Оказывается, на свете существуют криворукие хостеры, у которых на сервере нет REMOTE_ADDR (а точнее есть, но в нем лежит... адрес самого сервера!). И пихают они адрес удаленного хоста кому куда бог на душу положит. Некоторые - вы будете смеяться - в HTTP_X_FORWARDED_FOR. Говорят, в некоторых больших программных продуктах есть даже специальная настройка для таких случаев - "Получать IP-адреса из заголовка X_FORWARDED_FOR".
    Разумеется, этот курьёз не опровергает сказанного выше, и не стоит кидаться писать автоматические определители IP с его учетом. Все подобные случаи должны разбираться только в ручном режиме, самим программистом. Который сначала убедится - где именно в HTTP_X_FORWARDED_FOR лежит нужный адрес - в начале цепочки запятых или в конце, напишет правильный рег, и только потом в настройки сайта добавит код
    $_SERVER['REMOTE_ADDR']=get_ip_from_xff();

    Примечание для хостеров: mod_realip или mod_rpaf
    Примечание для пользователей: разумеется, таких хостеров надо избегать, как калёного железа. Наверняка ведь это не единственная их криворукость?

    В начало раздела Наверх


    Проблемы с кодировкой в MySQL версий 4.1+

    Быстрые рекомендации
    Подробные объяснения.
    Перекодировка
    Исправление БД, в таблицах которой неверно указана кодировка
    Если все равно ничего не получается
    Примітки
    ОПС
    Коментарі

    Быстрые рекомендации
    Если из базы выводятся вопросики, то после соединения с сервером выполняем волшебный запрос
    set names кодировка
    Параметр "кодировка" должен соответствовать кодировке, в которой выводятся страницы на сайте.
    наприклад:
    set names utf8
    Если это не помогло, и всё равно идут вопросики или крокозябры - значит, криво настроена кодировка таблиц. В этом случае см. рис.1 п. "Исправление БД" ниже.
    Если выводится нормально, но сортировка хромает - см. туда же.

    Подробные объяснения.
    До версии 4.1 кодировку данных в MySQL можно было задать только одну на всех. Теоретически, ничто не мешало в одной таблице хранить данные в юникоде, а в другой - в KOI-8. Так все и поступали: в конце концов, для БД любые данные - это всего лишь набор цифр. Что в неё положил - то она тебе и вернёт. Но правильный поиск и сортировка работали только для данных, которые были в кодировке, совпадавшей с настройками MySQL.

    Начиная с версии 4.1 стало можно хранить данные в любой кодировке, и задавать свой порядок сортировки хоть для каждого поля в таблице. Но для этого понадобилось ввести некоторые правила.

    Во-первых, для каждого поля в таблице были введены два параметра: кодировка (CHARACTER SET) и правила сравнения (COLLATION).
    Кодировка - это понятно. Говорим базе, в какой кодировке лежат наши данные.
    Правила сравнения задают порядок сортировки и сравнения данных при поиске.
    COLLATION жестко привязан к CHARACTER SET-у и может быть задан только из поддерживаемых кодировкой. Проще говоря, начало названия COLLATION должно совпадать с CHARACTER SET. К примеру, для кодировки utf8 можно задать правила сравнения utf8_bin, но нельзя cp1251_bin.
    Обычно у каждой кодировки есть, как минимум, два набора правил сравнения - имякодировки_bin и имякодировки_general_ci . Первый сравнивает в лоб по кодам символов, а второй - регистронезависимо, учитывая совпадающие символы. COLLATION имякодировки_general_cs сравнивает регистрозависимо, отличаясь от _bin тем, что учитывает совпадающие символы ("е" и "ё" в русском языке), а также, при сортировке, ставит на место те символы, которые идут в кодировке не по порядку (например "ё" в 1251).
    Если для поля не указан COLLATION, то он берется по умолчанию. К примеру, для utf8 - utf8_general_ci. В большинстве случаев COLLATION по умолчанию устраивает пользователя, а это значит, что его задавать не нужно. То есть, достаточно указать только кодировку.
    Кодировка может быть задана для поля, таблицы, database и для всего сервера. Установки имеют характер умолчаний, и могут быть изменены на любом уровне:
    Кодировку (и сортировку) можно указывать для каждого отдельного поля. Если они не указаны, то при создании таблицы берутся из кодировки, указанной для этой таблицы. Если при создании таблицы кодировка не указывается, то она берется из параметров database. Так же и при создании database - либо задаются явно, либо берутся из параметров сервера.

    Во-вторых, появилась необходимость говорить базе данных, в какой кодировке мы записываем или хотим получить свои данные. То есть, появилось такое понятие, как кодировка клиента . Здесь и кроется ответ на вопрос - "откуда берутся "вопросики"? Они появляются, если кодировка таблицы не совпадает c кодировкой клиента.
    Соответственно, в MySQL появились две новые команды
    set character_set_client
    и
    set character_set_results

    Первая указывает, в какой кодировке приходят данные в базу, а вторая - в какой их выдавать. Поскольку чаще всего эти кодировки совпадают, то можно писать короче - один запрос "SET NAMES кодировка", который и устанавливает оба эти параметра.

    Из приведенных объяснений должно быть ясно, что для беспроблемной работы нам надо сделать всего две вещи:
    1. Указывать правильную кодировку клиента.
    Это можно сделать либо в настройках сервера в my.ini, либо тем самым запросом SET NAMES.
    2. Создавая таблицы, не забывать указывать правильную кодировку для них.
    Це можна зробити декількома способами.
    Самое простое - это указывать кодировку и правила сравнения прямо в коде CREATE TABLE. приклад:

    CREATE TABLE `chartest` (
    `name` varchar(10) default NULL
    ) ENGINE=MyISAM CHARACTER SET=utf8


    Но что, если у нас огромный дамп на сотни таблиц, сделанный в прошлой версии MySQL? Дописывать к каждой таблице вручную? Возможно, это и придется делать. Но сначала надо попробовать установить параметры по умолчанию.
    Как мы помним, при создании таблиц, если для них не указывается collation и charset, эти параметры берутся из настроек database.
    Следовательно, надо попытаться изменить эти настройки.
    сначала смотрим, какие они сейчас: заходим в консоль и пишем

    use `mydb`
    show variables like "character_set_database";

    Этот запрос выведет кодировку базы mydb по умолчанию.
    Если она нас не устраивает, то пытаемся переопределить настройки самостоятельно

    alter database `mydb` character set utf8;

    Если запрос прошел успешно, то проверяем ещё раз, и, если все нормально, то начинаем создавать таблицы или заливать дамп.
    Если таким образом сделать не удалось (не хватает прав), то варианта только два - или обращаться к провайдеру, чтобы он сам поменял настройки, или дописывать COLLATION И CHARACTER SET ко всем создаваемым таблицам вручную.

    Перекодировка
    Как следует предыдущих объяснений, кодировка клиента должна соответствовать реальной кодировке поступающих данных. В этом случае, даже если данные лежат в другой, то всё равно никаких проблем не будет - MySQL автоматом перекодрует туда и обратно.
    Проведем эксперимент. Для него нам потребуется MySQL, установленная под Windows. Для тех, у кого другая ОС, я думаю, поменять кодировки в терминале проблемы не составит.
    Для демонстрации возможностей перекодировки воспользуемся тем фактом, что по умолчанию консоль windows настроена на старую кодировку DOS - 866. То есть, сначала мы создадим таблицу в этой кодировке и запишем в неё данные, а потом попробуем общаться с базой в другой кодировке.

    Сначала запустим командный интерпретатор cmd.exe и установим в свойствах окна шрифт Lucida Console.
    затем вызываем консоль mysql:
    C:\MySQL\bin\mysql.exe -uroot test
    В консоли пишем:

    set names cp866;
    CREATE TABLE ct (`name` varchar(10) default NULL)CHARACTER SET=cp866;
    insert into ct values ('Вова');
    select * from ct;


    Если мы все сделали правильно, то вывод будет таким:
    +------+
    | name |
    +------+
    | Вова |
    +------+


    дальше пишем exit , выходим из консоли, и пишем команду
    chcp 1251
    которая сменит кодировку окна консоли windows на 1251
    затем снова запускаем консоль mysql и пишем:

    set names cp1251;
    select * from ct;
    insert into ct values ('Вова');
    select * from ct;


    То же самое можно повторить и для кодировки utf8 (chcp 65001).
    В результате мы видим, что даже тогда, когда данные поступают не в той кодировке, в которой они хранятся в базе, работа с ними происходит совершенно корректно. При этом они продолжают лежать в базе в той же самой кодировке, в которой они были с самого начала - 866.

    Возможности перекодировки ограничиваются, разумеется, одним и тем же языком. То есть, из 1251 можно перекодировать в 866, в koi8r, в UTF8. В latin1 из 1251 перекодировать нельзя - появятся вопросики.

    Исправление БД, в таблицах которой неверно указана кодировка
    Или что делать, если буквы нормальные, а поиск и сортировка работают странно.

    Итак, у нас есть проблемы, которые не решаются запросом SET NAMES. Это значит, что в таблицах лежат данные в одной кодировке, а указана для этих таблиц - другая. В принципе, быстрое решение этой проблемы можно вывести из предыдущих объяснений - сделать запрос SET NAMES с кодировкой, которая указана в таблице. Посмотреть её можно запросом show create table `table` .
    Если там в последней строчке написано DEFAULT CHARSET=latin1, то выполняем запрос SET NAMES latin1
    В таблице не будет работать толком сортировка и поиск, но хотя бы сами данные будут отдаваться и записываться нормально (если кодировка html страницы соответствует фактической кодировке лежащих в базе данных). Но это, конечно, ненормальная ситуация, тем более, что исправить её совсем несложно.
    Что мы сейчас и проделаем.

    Для исправления существует два способа, которые описаны по ссылкам внизу.
    Я воспользуюсь вариантом из FAQ сайта linux.by, приведя только его содержательную часть. Но настоятельно рекомендую прочитать подробный вариант, с объяснением опций и исправлением возможных ошибок .

  • Узнаём кодировку таблиц (show create table `table`).
  • Делаем дамп БД с помощью mysqldump
    Допустим, мы выяснили, что таблицы были созданы по умолчанию в кодировке latin1, а фактически в них содержатся данные в utf8. В этом случае используем команду:
    mysqldump -uUSERNAME -pPASSWORD DB_NAME --allow-keywords --create-options --complete-insert --default-character-set=latin1 --add-drop-table > dump.sql
    Распространненая ошибка в таких случаях - когда в --default-character-set указывают фактическую кодировку данных, в данном случае - utf8. В дампе будет мусор. Указывать надо ту, которая установлена в таблицах. В результате MySQL не будет пытаться данные перекодировать, и отдаст как есть.
  • Просматриваем файл с дампом, чтобы убедиться, что в файле нормальные данные в кодировке utf8, а не мусор.
  • Целый и проверенный дамп копируем в сторону. Далеко в сторону.
  • В файле дампа поправляем операторы CREATE DATABASE и/или CREATE TABLE для создания таблиц в правильной кодировке. Либо меняем настройки database, как это было описано выше.
  • заливаем дамп обратно:
    mysql -uUSERNAME -pPASSWORD DB_NAME --default-character-set=utf8 < dump.sql
  • в код сайта, после функций mysql_connect и mysql_select_db добавляем строчку
    mysql_query("SET NAMES utf8") ;
  • Всё, можно работать!

    Если все равно ничего не получается
    Большое количество вопросов по кодировкам, не имеющих отношения к БД, побудило меня составить небольшое сводное руководство. Итак, кодировка нашего сайта складывается из 4 пунктов:
    1. Кодировка базы данных.
    • Задается при создании таблиц.
    • Может быть любая. Должна отражать реальную кодировку данных в таблице.
    • Например, если данные у нас будут лежать в кодировке Windows-1251, то создавая таблицу, пишем CREATE TABLE chartest (string text) DEFAULT CHARSET=cp1251;
    • Проверить текущую кодировку таблицы можно запросом SHOW CREATE TABLE tablename
    2. Кодировка клиента БД (клиентом в данном случае является скрипт, работающий с БД).
    • Задается сразу после соединения с БД, запросом SET NAMES кодировка
    • Должна совпадать с кодировкой выводимой HTML страницы.
    • Например, если страница у нас в utf-8, то в PHP пишем mysql_query ( "SET NAMES utf8" );
    • Проверить можно запросом show variables like '%char%'; (переменные character_set_client, character_set_connection и character_set_results должны иметь установленное нами значение)
    3. Кодировка сайта.
    • Задается HTTP заголовком Content-type.
    • Должна соответствовать кодировке данных на странице.
    • Например, если страница у нас в utf-8, то в PHP напишем header ( "Content-Type: text/html; charset=UTF-8" );
    • Проверить можно, выбрав в браузере пункт меню Вид - кодировка. Так же полезно посмотреть, какую кодировку шлет сервер в заголовке.
    4. Кодировка данных на странице.
    • Задается в редакторе.
    • Должна соответствовать той кодировке, которую мы хотим.
    • Особых рекомендаций здесь дать невозможно, но хотя бы минимальная компьютерная грамотность, чтобы выбрать в редакторе желаемую кодировку при сохранении, автору сайта необходима.

    NB: Обозначения кодировок в mysql могут не совпадать с общепринятыми. Внимательно смотрите примеры

    Примечания
  • Решая проблемы с кодировкой БД, не забывайте указывать правильную кодировку САЙТА!
    Во-первых, убедитесь, что сервер отдает правильную кодировку в HTTP заголовке и в meta теге.
    Во-вторых, если в коде присутствуют перекодировки, то убедитесь, что они работают, и работают правильно. А ещё лучше - вообще уберите их, и пользуйтесь средствами БД.
    И вообще - старайтесь отличать проблемы браузера от проблем с БД.

    ОПС
    Если бы я нашел этот текст чуть раньше, то эта статья не была бы написана. Все очень толково и подробно: http://www.linux.by/wiki/index.php/FAQ_PHP_MySQL_charset
    Еще один неплохой текст по кодировкам: http://mysqlfaq.wikispaces.com/Encoding
    Официальная документация, разумеется. Я, конечно, знаю, что ни один из читателей этого фака не пойдет читать документацию на английском... Но сам я брал информацию именно оттуда. http://dev.mysql.com/doc/refman/5.0/en/charset.html

    В начало раздела Наверх


    Пример кода, работающего с MySQL


    Самая классическая задача при работе с базой данных - это приложение вида форма-таблица.
    Таблица отображает записи, лежащие в БД, а форма служит для их добавледния/редактирования.

    Вот код скрипта, который и реализует упрощенную схему такого приложения.
    Кроме уникального идентификатора строки id, в таблице имеется только одно текстовое поле name
    Удаление в данном примере не предусмотрено, но при желании, его нетрудно добавить. Особенно учитывая тот факт, что записи желательно не удалять, а помечать, как непоказываемые. то есть, удаление сведется к апдейту.


    <?
    mysql_connect
    ();
    mysql_select_db ( "new" );
    $table = "test" ;
    if(
    $_SERVER [ 'REQUEST_METHOD' ]== 'POST' ) {
    if (
    $id = intval ( $_POST [ 'id' ])) {
    $query = "UPDATE $table SET name='" . mysql_real_escape_string ( $_POST [ 'name' ]). "' WHERE id=$id" ;
    } else {
    $query = "INSERT INTO $table SET name='" . mysql_real_escape_string ( $_POST [ 'name' ]). "'" ;
    }
    mysql_query ( $query );
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'PHP_SELF' ]);
    exit;
    }
    if (!isset(
    $_GET [ 'id' ])) {
    $LIST =array();
    $query = "SELECT * FROM $table" ;
    $res = mysql_query ( $query );
    while(
    $row = mysql_fetch_assoc ( $res )) $LIST []= $row ;
    include
    'list.php' ;
    } else {
    if (
    $id = intval ( $_GET [ 'id' ])) {
    $query = "SELECT * FROM $table WHERE id=$id" ;
    $res = mysql_query ( $query );
    $row = mysql_fetch_assoc ( $res );
    foreach (
    $row as $k => $v ) $row [ $k ]= htmlspecialchars ( $v );
    } else {
    $row [ 'name' ]= '' ;
    $row [ 'id' ]= 0 ;
    }
    include
    'form.php' ;
    }
    ?>

    form.php
    <form method="POST">
    <input type="text" name="name" value=" <?=$row [ 'name' ] ?> "><br>
    <input type="hidden" name="id" value=" <?=$row [ 'id' ] ?> ">
    <input type="submit"><br>
    <a href="?">Return to the list</a>
    </form>

    list.php
    <a href="?id=0">Add item</a>
    <? foreach ( $LIST as $row ): ?>
    <li><a href="?id= <?=$row [ 'id' ] ?> "> <?=$row [ 'name' ] ?> </a>
    <? endforeach ?>


    В начало раздела Наверх


    Как писать музыку на PHP


    Часто на форумах задают вопросы, как с помощью PHP выполнить ту или иную операцию. При этом скромному языку для домашних страничек приписывают чудодейственные свойства - по ожиданию авторов, в PHP должны быть встроенные функции, которые будут показывать кино, писать музыку, делать скриншоты с сайтов и многое другое.

    Нижеследующий текст призван ответить на можеcтво подобных вопросов разом.

    Ответ: Для большинства перечисленных задач в PHP встроенных функций нет. РНР - это язык обработки гипертекста. Или просто текста. Вот с текстом РНР работает великолепно. А если вы хотите работать с бинарными данными, то следует посмотреть в сторону специально предназначенных для этого утилит.
    Вся работа РНР сведется в этом случае к вызову внешней программы с помощью команды system() или её аналогов.

    Определить, существуют ли у РНР встроенные функции для решения той или иной задачи, можно с помощью документации. Просто посмотрев список функций . Если лень, то можно использовать тот же самый критерий: текстовый или бинарный у нас протокол. К примеру, HTTP, FTP, POP3, SMTP - это все текстовые протоколы. И РНР, в силу своей текстовой сущности, прекрасно с ними работает.
    Всякие картинки, видео, музыка - это бинарные данные. И для работы с ними стоит использовать внешние утилиты. Мне сейчас возразят, что с картинками PHP работать умеет. Ага, умеет. Полтора формата и imagestring. Граждане. Давно пора забыть про эту убогую библиотечку - в мире есть множество гораздо более продвинутых средств для работы с графикой. И все, что нужно для их освоения - это научиться работать с ними из командной строки, а потом аккуратно перенести эту строку в функцию system().

    Для справки:
    Для работы с видео - ffmpeg
    Для работы с графикой - ImageMagick

    Так же к этому разделу можно отнести многочисленные вопросы, посвященные скачиванию. Чего угодно и откуда угодно. Файлов с русскими буквами, файлов с пробелами в имени, файлов с зарегистрированными в браузере расшинениями (например html). Как заставить скачиваться или наоборот - как заставить открыть.

    Ответ на все эти многочисленные вопросы один. И очень простой. Решается в два счёта:
    1. Находим сайт, который реализует нужный нам функционал. Заодно проверяем - а возможно ли это в принципе. Если не нашли, то, скорее всего, нельзя. Если нашли, то
    2. Берем любую программу просмотра НТТР заголовков, выполняем требуемую операцию на найденном сайте и смотрим, какие заголовки он формирует. После этого формируем у себя такие же.
    Завдання вирішена.



    В начало раздела Наверх


    Пример системы управления сайтом

    См. статью http://phpfaq.ru/easy/


    В начало раздела Наверх


    Mail injection. Работа с e-mail средствами PHP.

    Що це таке?
    Формат e-mail
    захист
    Коментарі

    Что это такое?
    Это когда форма обратной связи на сайте используется для рассылки спама.
    Як? Cамый дурной вариант - когда скрипт позволяет пользователю подставить адрес получателя. Это уж совсем ни в какие ворота не лезет. И даже словом injection не назовешь - а просто головотяпство.
    Собственно injection - это когда адреса получателей подставляются в поля формы так, чтобы попасть в заголовки письма. К примеру, в заголовок.

    Говоря о mail injection, невозможно обойти вниманием сам принцип работы почтовых отправлений. Даже больше скажу: человек, который знает, как устроено электронное письмо, сразу понимает, в чем смысл таких инъекций, и как от них защищаться. Вообще, это та проблема, о которой я все время говорю: Давая новичку полное понимание предмета, ты отвечаешь ему на сто вопросов разом. А пичкать ответами на каждый отдельный вопрос - ну это же жутко непроизводительно! Не по-программистски!
    Что проще - посмотреть ответ в таблице умножения, или постоянно бегать к соседу с вопросами - сколько будет пятью пять, трижды семь?
    Что проще - один раз объяснить (а ещё лучше - отправить почитать) про формат письма или отвечать на сто вопросов про инъекции, кодировки, вложения файлов и так далее?

    Формат e-mail
    Итак, что собой представляет почтовое сообщение?
    Во-первых, это обычный текст. просто набор строк определенного формата. Которые можно читать и, при некотором навыке - понимать.
    Во-вторых, вы будете смеяться, но устроено оно точно по тому же принципу, что и наши веб-страницы.
    При запросе страницы по протоколу HTTP сначала идут заголовки, каждый на своей отдельной строке, а затем - тело ответа. Соответственно, заголовки от тела отделяются пустой строкой. Такой вот нехитрый формат. Сначала каждая строка воспринимается как заголовок, а как только встречается пустая - значит, дальше идет тело, которое надо показывать.

    В почтовом сообщении все устроено точно так же.
    Вообще, я считаю, что один раз посмотреть - это лучше, чем сто раз прочесть. А при работе с почтой исходный текст письма так же важен, как и исходник HTML страницы при разработке сайта на пхп.
    Здесь ещё одна проблема. Очень многие программисты просто не знают, что они хотят сделать. Я, говорит, хочу отправить письмо. Но ведь письмо - это ТЕКСТ! Казалось бы, чего проще - составил сначала текст, который хочешь получить, убедился, что этот текст работает - и генери точно такой же на пхп! Але немає. поскольку человек не знает, из каких элементов состоит исходный текст письма, и какой за что отвечает - то и тычется с вопросами: а почему у меня письмо крокозябрами? А почему у меня письмо нормальное, а заголовок крокозябрами - я ведь кодировку указал? А почему заголовок не "Вам письмо" а лабуда какая-то - =?koi8-r?B?98HNINDJ09jNzw==?= ???
    С SQL запросами и HTML текстом то же самое. Я, говорит, хочу меню на пхп. Милый - объясняешь ему - меню на пхп не бывает! Нарисуй какое хочешь меню на HTML, а потом пиши скрипт на пхп, который рисует такое же. Мне не надо - орет - на HTML! Подожду ответа более грамотного специалиста!

    Так же и здесь. Ну разберись один раз - что какой заголовок значит и как кодируется.
    В бате и в Outlook Express посмотреть исходник письма можно. В MS Outlook - только заголовки. В других клиентах не знаю. Но Outlook Express есть на любой виндовой машине, а пользователям других систем, я надеюсь, не нужно объяснять формат почтовых сообщений. Поэтому рекомендую создать письмо в OE, зайти в отправленные, нажать Свойства - Подробно - Исходное сообщение.
    Да, очень рекомендую перед этим в настройках выставить формат отправления, как "просто текст". иначе исходник будет гораздо сложнее для понимания. Что мы там увидим?
    From: "phorror" <[email protected]>
    To: =?koi8-r?B?98HNINDJ09jNzw==?= <[email protected]>
    Subject: =?koi8-r?B?98HNINDJ09jNzw==?=
    Date: Wed, 13 Jun 2007 10:08:32 +0400
    MIME-Version: 1.0
    Content-Type: text/plain;
    charset="koi8-r";
    Content-Transfer-Encoding: 7bit
    X-Priority: 3
    X-MSMail-Priority: Normal
    X-Mailer: Microsoft Outlook Express 6.00.2900.3028
    X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3028

    test
    test
    test


    на заголовки, начинающиеся с X, обращать внимание не надо - это необязательные.
    главное, что мы видим:
    1. Все, как я и рассказывал - сначала заголовки, потом пустая строка, потом текст
    2. Формат заголовка: начинается с новой строки, дальше идет ключевое слово, дальше двоеточие, пробел и значение (опять очень похоже на НТТР заголовки!)
    3. Формат указания e-mail адресов. Кроме имени, понятно без перевода. про имя ниже.
    4. формат указания заголовка. Ничё непонятно. Хотя, если не ужасаться, а сесть и немножко подумать, то можно сообразить, что, во-первых, такая лабуда применяется для форматирования строк в кодировках, отличных от latin1, а, во-вторых, формат-то у них совсем простой. Вопросительные знаки разделяют разные поля (точь-в точь как палочки | или пять троеточий в твоей первой гостевой книге). Первым полем идет явно кодировка языка. Вторым - можно догадаться - формат кодирования текста. Там может быть B или Q. (base64 и Q-encoding соответственно). А дальше - сам текст заголовка.
    Следовательно, понятен и алгоритм раскодирования: разбиваем по вопросам, раскодируем текст (функции есть в пхп, для Q подойдет от quoted-printable, как я понимаю), перекодируем язык если надо. Усе. Для сборки своего заголовка - обратная операция.
    Если бы сабжект был английский, то он выглядел бы просто:
    Subject: This is a test letter
    5. Поле Content-Type показывает нам, что заголовок может состоять не из одной строки, а может продолжаться на следующих - для этого дополнительные строки должны начинаться с пробельного символа. А так же, что она отвечает за кодировку текста письма, указывает формат - текст или html, и ведает ещё одной очень важной функцией: отвечает за структуру сложных, multipart сообщений, которые мы рассматривать здесь не будем.

    Из всего вышесказанного видно, что научиться отправлять собственные письма (те, которые формирует скрипт, а не из заполненной формы) очень просто:
    1. Пишем в аутлуке ровно такое же письмо, которое хотим отправлять скриптом.
    2. Смотрим заголовки.
    3. Пишем скрипт, который формирует точно такие же.
    4. Подсталяем эти заголовки в нужные поля функции mail
    5. Готово!
    И никаких проблем с кодировками самого письма, заголовоков, имени отправителя! слово "крокозяблы" пропадает из нашего лексикона!
    И с SQL запросами все ровно то же самое. новички часто пытаются сразу писать скрипт, который формирует сложный запрос, даже не представляя себе в точности, как этот запрос должен выглядеть! Запрос сначала надо написать и отладить в консоли или графическом клиенте. А потом писать пхп скрипт, который формирует точно такой же текст запроса.

    Усе. Теперь можно переходить к инъекциям.

    Защита
    Исходя из структуры письма можно сделать простой вывод: используя переводы строк, можно добавить любое количество новых заголовоков, в том числе - и адресов получателей.
    то есть, если мы напишем в своем коде
    $subject=$_POST['subject'], а в этом поле будет написано сто адресов получателей, разделенных переводами строк, то письмо отправится сотне жертв. просто, как два байта переслать.

    Отсюда следует, что и защита форм обратной связи такая же простая.
    Самое надёжное - помещать введенную пользователем информацию только в текст письма, и никуда больше. Сам я использую именно этот метод. Мне он нравится своей простотой и надежностью. А чем решение проще - тем оно мне милее.

    Если же ну прямо так уж хочется эмулировать отправку "настоящего" емейла - с заголовком, адресом отправителя и так далее, то все данные из формы, которые вставляются не в текст сообщения, следует обязательно проверить на наличие символов "\r" и "\n"!
    И при наличии таких символов письмо не отправлять.
    Адрес получателя, разумеется, дожен быть жестко прописан в скрипте (видал я некоторых уникумов, писавших адрес в скрытое поле в форме).

    Усе.

    В начало раздела Наверх


    Безопасность PHP скриптов

    файли
    SQL Injection
    Eval
    Mail Injection
    XSS
    CSRF, Cross-site Request Forging
    HTTP Injection
    Инициализация переменных
    Заливка файлов на сервер
    ОПС
    Коментарі

    В принципе, я не большой специалист по безопасности. Да и тема настолько обширная и многогранная, что одной заметкой её охватить невозможно. Поэтому я сначала писать не собирался. Но со временем выяснилось, что даже очевидные вещи неизвестны многим программистам. Поэтому я решил собрать здесь несколько рекомендаций самого базового уровня.

    Основной принцип написания безопасных программ - это контроль данных, приходящих от пользователя.
    Есть несколько основных типов атак, которым может подвергнуться сайт, и мы их сейчас рассмотрим.

    файли
    В хорошем современном сайте уже редко можно встретить передачу имени файла в скрипт - данные хранятся в базе, а модули подключаются по более сложным алгоритмам, чем просто передача имени файла в адресной строке. Однако начинающие программисты просто без этого не могут. Доходит до анекдота - "хочу, чтобы было index.php?module=news потому что news.php - не солидно, сайт не выглядит крутым!". Ну, на что не пойдешь ради того, чтобы выглядеть круто. Надо только не забывать о безопасности. О том, что подсунуть для инклюда можно через адресную строку что угодно - от файла на другом сервере (с любым пхп-кодом!), до файла на своем - с настройками или паролями.

    Для борьбы с такими подстановками есть несколько методов. Можно проверять имя файла регулярками, но мне ближе всего функция basename() , которая отрезает от имени файла все лишнее.
    Если надо передавать файл вместе с путем к нему, к примеру, themes/green/balloons.php, то проверка будет сложнее. Поэтому я бы рекомендовал, все-таки, передавать только имя файла. Или не передавать ничего.

    SQL Injection
    Следующий тип атак довольно подробно рассмотрен в уже имеющейся статье на этом сайте, \"Кавычки \". Cоставление запросов, слеши, SQL Injection . Хочется лишь добавить, что защита от инъекций - не главное в соблюдении правильного синтаксиса, а побочный продукт, и синтаксис надо соблюдать всегда. Особенно - учитывая инъекции второго порядка, когда данные в запрос подставляются не от пользователя, а уже лежащие на сервере.

    Eval
    Удивительно, но многие программисты не осознают того факта, что применять eval к пользовательским данным - это все равно, что лично вручать вору ключ от сейфа. Этой функцией и так-то следует пользоваться очень осторожно, а на обработку ей введенных пользователем данных и вовсе должен быть жесткий запрет.
    При этом следует понимать, что пользовательские данные остаются таковыми и после того, как пользователь непосредственно отправил их в скрипт. Полученные из базы данных при последующих исполнениях скрипта, они не становятся менее опасными.

    Mail Injection
    Получившая распространение в последнее время атака так же уже рассмотрена на этом сайте, Mail injection. Работа с e-mail средствами PHP. . Коротко можно сказать, что нельзя допускать наличие переводов строки в данных, которые попадут в заголовки письма.

    XSS
    Тип атаки, так же получивший распространение сравнительно недавно. В отличие от всех предыдущих, которые атакуют непосредственно код скрипта, XSS атакует его функциональность: заставляет выполнять пользователя действия, нужные хакеру, или ворует его, пользователя, данные.
    Методы весьма разнообразны и многочисленны, однако наиболее распространенный и опасный - это внедрение javascript инструкций в безобидные теги - <img>, <a> и так далее.
    Наиболее действенным методом борьбы с этим является полный запрет на ввод пользователем какого бы то ни было HTML кода. Методов борьбы с этим достаточно много, но следует помнить, что функция strip_tags() не убирает опасные вставки из разрешенных тегов. поэтому лично я предпочитаю пользоваться функцией htmlspecialchars() , при выводе любых пользовательских данных.
    Но главная проблема при защите от XSS - не сам метод, а последовательность его применения. практически все известные атаки были произведены в тех частях сайта, где никто и подумать не мог об обработке пользовательских данных. А даремно. Обрабатывать их надо везде и всегда.

    Так же к этому разделу можно отнести атаки на сессии. Связанные с кражей или подстановкой идентификатора. подробнее можно почитать в разделе Сессии. Подробное описание работы и объяснение механизма.

    CSRF, Cross-site Request Forging
    Формирование на сайте хакера запроса, который будет выполнен браузером пользователя (и, как следствие - со всеми правами пользователя!). То есть, просто авторизация от этой атаки не спасает. Для защиты от этого типа атак следвет взять за правило добавлять ко всем важным формам скрытое поле, содержащее уникальную строку (сгенерированную случайным образом только для этой формы), записывать её в сессию и сравнивать при обработке формы. Хакер добавить такую строку в свою форму не сможет, и атака не сработает.

    HTTP Injection
    XSS уязвимость, похожая на mail injection, только объектом атаки являются не заголовки письма, а HTTP-заголовки. Атака экзотическая, но знать про неё надо. Хакеру ничего не стоит дописать к урлу код с яваскриптом, который благополучно через переменную PHP_SELF или REQUEST_URI программист сам вставит в свою страницу.

    Инициализация переменных
    Со времени появления PHP в нем есть одна очень неприятная функциональность: получить пользовательские данные в своем скрипте можно совсем того не ожидая. Да-да, речь идет о register_globals. Если эта директива включена, пользователь может создать в скрипте любую переменную с любым содержимым, если только она не была создана программистом. А программисты часто это делать забывают, к примеру, если мы имеем код
    if ( $login == "login" AND $password == "password" ) $admin = 1 ;
    и в дальнейшем проверяем переменную $admin, то любой может получить права админа, просто дописав в адресной строке index.php?admin=1.
    Лекарством от этого служит принудительная инициализация всех переменных перед использованием. В строгих языках, таких, как Паскаль, невозможно использовать переменную, предварительно не объявив. В PHP это возможно, но злоупотреблять этим не стоит - следует объявлять все переменные перед использованием. В качестве же временной заплатки можно порекомендовать register_globals=off.
    то же самое можно почитать на офсайте PHP, http://ru.php.net/manual/ru/security.globals.php

    Отдельной строкой следует упомянуть борьбу с register_globals. Попытки эмулировать её включение (если отключена) или нейтрализовать (если включена) приводят к обратным результатам. Многочисленные циклы с назначением или удалением переменных приводят к проблемам более худшим, если бы директива просто была установлена в желаемое значение. Единственный гарантированный способ избежать её влияния - инициализация переменных.

    Заливка файлов на сервер
    Поскольку сервер различает файлы по их расширению, то расширение проверять обязательно, наряду с другими проверками, такими, как Getimagesize и проч.
    Вообще, читая форумы, поражаешься странному стремлению выбрать какое-то одно решение, и жутким спорам, какое лучше (при том, что ни одно не является исчерпывающим). Вместо того чтобы взять, и использовать все вместе.

    ОПС
    Статья Сергея Круглова, http://www.captcha.ru/articles/antihack/


    В начало раздела Наверх


    Базовые понятия MySQL и отличия от текстовых файлов.

    FAQ. Порядок записей, первая и последняя.
    FAQ. ID.
    Язык SQL.
    FAQ. Перенос данных между разными серверами.
    Практическое использование.
    Коментарі

    FAQ. Порядок записей, первая и последняя.

    Список - это текстовый файл. Или, скажем, список учеников в классном журнале.
    чтобы получить упорядоченный список, мы должны, при добавлении любой записи, список переупорядочивать. При добавлени любой! А если мы хотим иметь два и больше видов упорядочивания? К примеру, список учеников у нас составляется по алфавиту, но нам интересно посмотреть его по успеваемости. Заводить два списка? НІ! Завести базу данных.
    И понять первый, самый основополагающий принцип. Порядок у данных бывает только при выборке их из БД ! Это очень важный факт. Данные в БД хранятся не как в классном журнале - по порядку. А как в песочных часах - кучей. Не задашь порядок вывода - будут выводиться от балды. Сама база не запоминает, какая запись была первой, а какая - последней. Но зато она умеет сортировать по признакам, указанным пользователем при создании таблицы.

    Первое, что надо запомнить . В базе не бывает первой и последней записи. О порядке можно говорить только при выводе, если пользователь сам задал этот порядок. к примеру, отсортировав записи по алфавиту. Вот в такой выборке есть и первая и последняя.
    Если нам нужно знать порядок занесения записей в базу - добавьте поле, с помощью которого это можно определить.

    Следующее, чем отличается БД от файла - это произвольный доступ. В файле, для того, чтобы обратиться к строке в середине, надо перебрать по очереди все идущие до неё. При обновлении - еще хуже. Для того, чтобы изменить строчку в строковом файле - надо переписать его ВЕСЬ! Цілком. С БД же - все просто. Хотим выбрать одну строчку - выбираем! Хотим изменить одну строку - изменяем. Хотим удалить две - удаляем!
    Примечание: На самом деле, БД - это не волшебный ящик, и хранит она данные не в нематериальном эфире, а в тех же самых файлах. И точно так же их переписывает. Но делает это она совершенно прозрачно для пользователя.

    При произвольном доступе и отсутствии внутренней нумерации «первый-второй-третий», перед нами встает проблема идентификации строк. Допустим, мы вывели строки из базы на экран, и теперь, кликнув по ссылке, хотим отредактировать одну из них. Как её запросить? По содержимому одного из полей? А вдруг это содержимое совпадает с еще несколькими записями? Нам нужен уникальный идентификатор! В MySQL применено остроумное решение. Одно из полей, обычно называемое `id` является автоинкрементным, то есть, автоматически увеличивающимся. При добавлении каждой новой записи, если id не указать или значение равно нулю, база присвоит полю id значение, на единицу большее, чем у предыдущего. Так мы получили уникальный идентификатор – ни у одной строки в этой таблице id не будет совпадать. Теперь очень просто отредактировать или вывести любую запись – достаточно указать её id.
    Кстати, у автоинкрементного id есть еще одно побочное свойство. Отсортировав таблицу по этому полю, можно получить ту самую пресловутую первую или последнюю запись Smile happy

    Однако, с этим полем связано множество ошибок и непониманий.

    FAQ. ID.
    Во-первых, это поле ошибочно принимают за нумератор. Как в классном журнале – 1,2,3-й ученик... А если ученика исключили из школы? А если мы выбираем не всех учеников, а только мальчиков?
    Правило первое: id к нумерации не имеет ни малейшего отношения! Во-первых, потому, что id могут идти не по порядку, а во-вторых, что все равно у нас порядок бывает только при ВЫБОРКЕ. Которая может быть какой угодно, id в ней могут идти совершенно вразнобой! Если мы хотим пронумеровать результаты – пожалуйста, при выводе добавим код на PHP, который будет это делать. В ЭТОЙ выборке. Нумеровать надо при выводе. Именно потому, что вариантов выборки может быть бесконечное количество.
    Посмотрим, для чего еще новичку может потребоваться нумерация? Для определения количества выбранных записей. Для этого есть функция – mysql_num_rows(). Она нам поможет при ЛЮБОЙ выборке. В то время, как нумерация не поможет нам вовсе, по причинам указанным выше.
    Правило второе: Менять id записи нельзя никогда, ни под каким видом. Во-первых, это просто никогда не нужно. Во-вторых, это же у нас УНИКАЛЬНЫЙ идентификатор. Поле id лишь внешне напоминает цифры. На самом деле – это способ ОДНОЗНАЧНО, и в любое время идентифицировать запись. Допустим, у нас есть сайт с новостями. Кто-то поставил ссылку на новость с id=1. Потом мы этот id сменили. В результате человек придет по ссылке не туда. Если же вам необходимо id перенумеровать – значит, вам просто не нужно автоинкрементное поле.

    Язык SQL.
    Теперь немного о языке SQL. Это, как я уже говорил – гениальное изобретение человечества. Практически искусственный интеллект. Посудите сами.
    Длял того, чтобы выбрать имена всех учеников школы, учащихся в классе 7А, отсортировав их по алфавиту, надо написать такой запрос:
    SELECT lastname FROM school WHERE klass='7A' ORDER BY lastname
    Это практически осмысленное предложение на английском языке!
    Попробуем сделать подстрочный перевод:
    ВЫБРАТЬ фамилии ИЗ школы [такие,] ГДЕ класс равняется 7А, ОТСОРТИРОВАВ ФАМИЛИИ ПО АЛФАВИТУ
    Ну как - неплохо? Мы практически натуральным языком говорим базе, что нам надо – и она выдает нам нужные строки в нужном порядке! И все это ОДНОЙ СТРОЧКОЙ! При этом сложность выборки может быть любой. Если бы мы выбирали из текстового файла – нам бы пришлось написать программу не на один лист. А тут – одна строчка!
    Ну як? Вы еще хотите колупаться с файлами?

    FAQ. Перенос данных между разными серверами.
    Перенос данных посредством дампа - файлов, содержащих запросы INSERT.
    То есть, на том сервере, с которого мы хотим перенести базу, мы генерируем файл с запросами INSERT для каждой строки каждой таблицы базы данных. А на удалённом сервере просто исполняем эти запросы.

    Наиболее правильный и прямой путь получить дамп - это воспользоваться утилитой командной строки mysqldump
    заходим в DOS или в shell и пишем
    mysqldump -u<логин> -p<пароль> база > dump.sql
    получаем файл dump.sql, который следует перенести на удалённый сервер и исполнить там.
    для этого надо вызвать mysql shell таким образом:
    mysql -u<логин> -p<пароль> база < dump.sql
    всё, база перенесена.
    при отсутствии доступа к шеллу можно воспользоваться PHP скриптом Sypex Dumper

    Практическое использование.
    Очень хорошая подборка материалов, обучающих работе с БД есть на сайте "PHP в деталях".
    Это уже упоминавшаяся здесь статья Вадима Ткаченко
  • "Вступление в PHP и MySQL" .

  • А так же статьи Дмитрия Лебедева:
  • Работа с базами данных. Початок.

  • Работа с MySQL: Подробнее

  • Работа с MySQL. Новостная лента для странички

  • Так же, при работе с MySQL необходимо учитывать требования, изложенные в разделе этого FAQ, посвященном составлению запросов и использованию кавычек

    Остальные материалы, посвященные MySQL:


    В начало раздела Наверх


    малювання календаря


    Функция выводит календарь на месяц и принимает аргументом массив с датами в формате ГГГГ-ММ-ДД.
    Если дата совпадает в выводимой в календаре, она оформляется ссылкой
    Приклад використання:
    <?
    if (isset( $_GET [ 'date' ])) echo "выбрана дата " . $_GET [ 'date' ];
    my_calendar (array( '2004-09-01' ));
    ?>

    код:
    <?
    function my_calendar ( $fill = '' ) {
    $month_names =array( "январь" , "февраль" , "март" , "апрель" , "май" , "июнь" ,
    "июль" , "август" , "сентябрь" , "октябрь" , "ноябрь" , "декабрь" );
    if (isset(
    $_GET [ 'y' ])) $y = $_GET [ 'y' ];
    if (isset(
    $_GET [ 'm' ])) $m = $_GET [ 'm' ];
    if (isset(
    $_GET [ 'date' ]) AND strstr ( $_GET [ 'date' ], "-" )) list( $y , $m )= explode ( "-" , $_GET [ 'date' ]);
    if (!isset(
    $y ) OR $y < 1970 OR $y > 2037 ) $y = date ( "Y" );
    if (!isset(
    $m ) OR $m < 1 OR $m > 12 ) $m = date ( "m" );

    $month_stamp = mktime ( 0 , 0 , 0 , $m , 1 , $y );
    $day_count = date ( "t" , $month_stamp );
    $weekday = date ( "w" , $month_stamp );
    if (
    $weekday == 0 ) $weekday = 7 ;
    $start =-( $weekday - 2 );
    $last =( $day_count + $weekday - 1 ) % 7 ;
    if (
    $last == 0 ) $end = $day_count ; else $end = $day_count + 7 - $last ;
    $today = date ( "Ymd" );
    $prev = date ( '?\m=m&\y=Y' , mktime ( 0 , 0 , 0 , $m - 1 , 1 , $y ));
    $next = date ( '?\m=m&\y=Y' , mktime ( 0 , 0 , 0 , $m + 1 , 1 , $y ));
    $i = 0 ;
    ?>
    <table border=1 cellspacing=0 cellpadding=2>
    <tr>
    <td colspan=7>
    <table width="100%" border=0 cellspacing=0 cellpadding=0>
    <tr>
    <td align="left"><a href=" <? echo $prev ?> ">&lt;&lt;&lt;</a></td>
    <td align="center"> <? echo $month_names [ $m - 1 ], " " , $y ?> </td>
    <td align="right"><a href=" <? echo $next ?> ">&gt;&gt;&gt;</a></td>
    </tr>
    </table>
    </td>
    </tr>
    <tr><td>Пн</td><td>Вт</td><td>Ср</td><td>Чт</td><td>Пт</td><td>Сб</td><td>Вс</td><tr>
    <?
    for( $d = $start ; $d <= $end ; $d ++) {
    if (!(
    $i ++ % 7 )) echo " <tr>\n" ;
    echo
    ' <td align="center">' ;
    if (
    $d < 1 OR $d > $day_count ) {
    echo
    "&nbsp" ;
    } else {
    $now = "$y-$m-" . sprintf ( "%02d" , $d );
    if (
    is_array ( $fill ) AND in_array ( $now , $fill )) {
    echo
    '<b><a href="' . $_SERVER [ 'PHP_SELF' ]. '?date=' . $now . '">' . $d . '</a></b>' ;
    } else {
    echo
    $d ;
    }
    }
    echo
    "</td>\n" ;
    if (!(
    $i % 7 )) echo " </tr>\n" ;
    }
    ?>
    </table>
    <? } ?>


    В начало раздела Наверх


    Обработка ошибок, часть 1. Общие принципы.


    Большинство начинающих пхп-программистов путаются в обработке ошибок.
    Причём путаница происходит оттого, что они смешивают несколько понятий. А саме:
    1. Факт ошибки.
    2. Сообщение системы об ошибке.
    3. Обработка ошибки
    4. Информирование пользователя об ошибке.

    Чаще всего путают второй и четвёртый пункты, принимая одно за другое.
    Так же, ради неправильного понимания четвёртого, кладут на предыдущие три.
    Ну и - коронный номер - борьба с ошибками путём подавления сообщений о них.

    Далее следует несколько простых и очевидных рекомендаций.

    Системное сообщение об ошибке - не твой враг, а твой друг. Избавляться от него не надо! Наоборот - надо стремиться получить его всеми силами - оно поможет исправить тебе ошибку .
    Не надо просто путать программиста с пользователем.
    Если ты разрабатываешь сайт, и пользователь - ты сам, то удобнее смотреть ошибки на экране.
    поэтому делаем в настройках сервера
    display_errors= on

    Если сайт уже работает, и на него заходит куча пользователей, то ситуация меняется в корне.
    Во-первых, системные сообщения об ошибках пользователь видеть не должен.
    Во-вторых, их как-то должен видеть программист, причём не только когда он сам обращается к сайту, но и те ошибки, которые происходят у других пользователей.
    Первая задача решается уже знакомой нам директивой
    display_errors= off
    вторая - настройкой, которая заставит пхп все ошибки писать в лог, где их потом может увидеть программист.
    log_errors= on

    С самописными функциями всё просто.
    главное - никаких die(mysql_error())!!!
    это хорошо на этапе обучения, но никуда не годится на посещаемом сайте!
    во-первых, ПОЛЬЗОВАТЕЛЮ эта mysql_error() ничего не скажет.
    во-вторых, программист её не увидит.
    В-третьих, негоже вообще обрывать вывод сайта на середине.
    Поэтому делаем проверку вместо die надо использовать trigger_error()
    В результате у нас должно получиться
    $query = "Select * FROM table" ;
    $res = mysql_query ( $query ) or trigger_error ( mysql_error (). $query )
    ;
    Таким образом, сообщение об ошибке выведется туда же, куда выводятся все остальные ошибки, в зависимости от установок, рассмотренных выше.

    Из написанного выше становится ясно, что собака не бывает нужна в принципе никогда.
    Во-первых, расставить собак во всех местах вероятного появления ошибки просто нереально.
    Во-вторых, и самое главное - собака делает НЕ ТО, ЧТО ВАМ НУЖНО! Вы просто путаете вывод сообщения пользователю и информирование программиста об ошибке.
    Вам нужно запретить вывод ошибок пользователю? Відмінно! ОДИН раз написать display_errors гораздо проще, чем лазить по коду, расставляя собак.
    Надо посмотреть сообщение об ошибке? Відмінно! Лезем в лог или включаем display_errors, вместо того, чтобы сидеть и гадать на кофейной гуще - где ошибка. Вывод же сообщений мы собакой подавили!

    Усе. С программистом закончили.
    Теперь осталось проинформировать пользователя об ошибке - ведь до сих пор мы заботились только о том, чтобы пользователь не увидел ошибку.
    Теперь подумаем, как сделать так, чтобы пользователь увидел дружественное сообщение об ошибке, да ещё и желательно не в разорванном дизайне.
    Проще всего это делается с использованием шаблонов.
    Ведь при их использовании сначала исполняется весь нужный код, по завершении которого можно проконтролировать успешность его выполнения, а потом выводится шаблон, который, в случае ошибки, можно заменить шаблоном стандартного сообщения об ошибке.
    Подробнее об этом можно прочитать в следующей главе: Обработка ошибок, часть 2. Разбор примера. Исключения.

    В начало раздела Наверх


    Обработка ошибок, часть 2. Разбор примера. Исключения.

    Пример из жизни.
    Использование исключений.
    Коментарі

    Пример из жизни.
    Вот яркий образчик "обработки ошибок", который можно встретить практически в любом скрипте
    if ( is_writable ( $file ) {
    $handle = fopen ( $file , 'w' ) || die( 'error opening' );
    fwrite ( $handle , $text ) || die( 'error writing' );
    fclose ( $handle );
    } else die(
    "not writable" );

    Почему это неправильно, было рассказано в первой части.
    Попробуем переписать этот код по-другому.

    $handle = fopen ( $file , 'w' );
    $written = fwrite ( $handle , $text );
    fclose ( $handle );
    if (
    $written === FALSE ) {
    $error = "Извините, произошла ошибка. Попробуйте повторить позднее"
    }


    Функция is_writable имеет смысл только в том случае, если планируется как-то реагировать на невозможность записи. А если для пользователя, как это часто бывает, существует только два состояния - "операция прошла успешно" и "произошла ошибка", то и дополнительная проверка ничем не поможет. А вот запись в лог конкретной причины невозможности записи - очень поможет программисту.
    поэтому мы убираем проверку is_writable, чтобы ошибки при открытии и записи файла пошли в лог, а для сообщения пользователю проверяем только самый конечный результат - запись в файл.

    однако это решение подходит не для всех случаев. иногда от наличия ошибки зависят очень большие куски кода, которые гарантированно будут выдавать ошибки, которые ничего не добавят к самой первой. К примеру, если на месте fopen будет mysql_connect, за которым идет десяток запросов, то после нужной программисту ошибки соединения в логе будет еще куча ошибок запросов.
    В таких случаях нужно обрабатывать эти "ключевые точки":
    if ( $handle = fopen ( $file , 'w' )) {
    $written = fwrite ( $handle , $text );
    fclose ( $handle );
    }
    if (!
    $handle OR $written === FALSE ) {
    show_error_page ();
    }


    Смысл здесь в чем?
    Как описано в первой части, мы должны разделять саму ошибку, сообщение об ошибке, информирование о ней пользователя и программиста .
    саму ошибку обрабатывает первый if - если файл не открылся, то записи не будет.
    сообщение об ошибке, как положено, не пойдет на экран, а пойдет в лог.
    программист будет проинформирован из лога же.
    А пользователь, которому неважно, какие у нас там были ошибки, а интересует только одно: записалось-не записалось - получает свое сообщение. Для этого мы проверяем то, что интересует пользователя - произошла ли, собственно, запись в файл.

    Использование исключений.
    В PHP5 появился стандартный для большинства языков механизм исключений.
    почитать про него можно в документации , а здесь мы просто посмотрим, как будет выглядеть код с применением этого механизма:
    try {
    $handle = fopen ( $file , "a" );
    if (!
    $handle ) throw new Exception ( "open" );
    fwrite ( $handle , $text );
    fclose ( $handle );
    }
    catch (
    Exception $e ) {
    show_error_page ();
    }


    Можно провести такую аналогию, что throw является аналогом die, но не для всего скрипта, а только для канарейки - кода, заключенного между try {}. Что позволяет, с одной стороны, не выполнять код, который все равно не сработает, а с другой - не обрывать работу всего скрипта, а завершить его корректно.

    Вообще, механизм исключений предоставляет просто неограниченные возможности по управлению ошибками.
    Взять, например, вот такой код:
    function exceptions_error_handler ( $severity , $message , $filename , $lineno ) {
    throw new
    ErrorException ( $message , 0 , $severity , $filename , $lineno );
    }
    set_error_handler ( 'exceptions_error_handler' );


    при его использовании наш тестовый пример становится совсем удивительным:
    try {
    $handle = fopen ( $file , 'w' );
    fwrite ( $handle , $text );
    fclose ( $handle );
    }
    catch (
    Exception $e ) {
    show_error_page ();
    }

    Видишь обработку ошибок? И я нет. А она есть!
    fwrite($handle, $text); не выполнится, если fopen отработала с ошибкой.

    Правда, применять такой способ следует очень аккуратно, поскольку исключение будет вызвано не там, где мы точно его хотим, а вообще любой ошибкой, даже нотисом на несуществующую переменную.

    В начало раздела Наверх


    Что такое PHP?


    Как ни странно, но все 5 лет, которые существует этот сайт, здесь не было такого раздела Smile happy
    попробуем восполнить этот недостаток.
    Данный раздел предназначается тем, кто собирается изучать PHP или просто интересуется - что это такое.

    PHP - это язык программирования .
    Язак программирования, предназначенный для создания сайтов. Или, другими словами, PHP позволяет автоматизировать работу с сайтом.

    Для чего вообще может понадобиться язык программирования при создании сайта? Кому он пригодится, а кому - не очень? Давайте посмотрим.

    Условно говоря, применение PHP можно разделить на три группы:

    1. Самое простое (но очень мощное) применение - это использование PHP, как аналога SSI. Записать все неизменяемые части сайта в отдельные файлы (меню, шапку, подвал) и вместо того, чтобы писать одно и то же на каждой странице, просто вызывать
    include "menu.php" ;
    Это уже значительно облегчит работу с сайтом. К примеру, если добавится новый пункт в меню...

    2. Следующий этап - создание отдельных небольших программок. Это может быть голосование, гостевая книга, вывод текущей даты, рисование календаря... Хотя последние две задачи, в отличие от первых, не обязательно делать на PHP - можно и на Яваскрипте. PHP применяется тогда, когда нужно какую-то информацию хранить на сервере. К примеру, гостевую книгу на Яваскрипте написать множно, но вот... увидит её сообщения только тот, кто их добавлял Smile happy

    3. Победное шествие PHP па планете началось в тот миг, когда кому-то пришла в голову замечательная идея: а почему бы не добавлять материалы на сайт не обычным способом, закачивая HTML файлы по FTP, а забивая текст в форму, как сообщение в гостевую книгу?
    Таким образом, пишется две программы, одна из которых позволет администратору добавить информацию на сайт, а вторая - показывает эту информацию посетителям. Таким образом устроены практически все современные сайты.

    С чем чаще всего путают PHP? С готовыми программами, написанными на PHP. Если вам нужна гостевая книга, портал или электронный магазин - вам не нужно учить PHP. Вам нужно найти готовую программу.

    Если же вы решили изучать этот язык, то следует знать, что ожидает впереди. Для того, чтобы стать даже начинающим веб-программистом, надо знать:
    - три языка программирования - HTML, PHP и SQL.
    - Иметь очень хорошее представление о протоколе передачи гипертекста HTTP и о том, как взаимодействует компьютер пользователя с веб-сервером.
    - иметь базовые представления об операционных системах (в частности понития файл, каталог, знать основные отличия windows от unix)
    - иметь базовые представления о сетевом протоколе TCP/IP (как соединяются компьютеры, что такое IP адрес, DNS и прочее)
    - обязательно придется познакомиться с понятием отладки своих программ

    Если весь этот объем не пугает, то добро пожаловать! Начните с раздела " Хочу изучать PHP "

    В начало раздела Наверх


    Как делать UPLOAD файлов на сервер

    См. статью http://phpclub.ru/detail/article/upload


    В начало раздела Наверх


    Як зробити зменшену копію картинки?

    См. статью http://phpfaq.ru/links#resize


    В начало раздела Наверх


    шаблони


    Нет, наверное, в веб-программировании темы столь же очевидной, сколь и непонятной, как шаблоны. Каждый, рано или поздно, приходит к выводу о необходимости испрользования шаблонов. Но приходит, почему-то, через какие-то дичайшие заблуждения и фантазии.

    Самое простое и очевидное заблуждение состоит в том, что новички называют шаблоном вынесенный в отдельный файл "дизайн" - обший html для всех страниц сайта. И на этом успокаиваются. Динамическую информацию, ничтоже сумняшеся, выводя старым добрым echo Smile happy
    На самом же деле, шаблонизатор в основном занимается выводом изменяющегося содержимого страниц сайта. А вывод "дизайна" - задача второстепенная.

    Фантазий главных две:
    1. Шаблоны нужны "дизайнеру", чтобы он мог их править, не разбираясь в PHP.
    2. Следовательно, шаблоны служат для отделения PHP от HTML.

    Давайте попробуем задуматься над первым утверждением. Кто такой дизайнер? Это человек, который работает в фотошопе. HTML он чаще всего не знает вообще. А над шаблоном работает либо специальный верстальщик или - чаще всего... сам программист! Смішно, правда?
    Теперь следствие, про отделение PHP от HTML. Відмінно. Перед нами стоит святая цель отделить. Поэтому мы придумываем Смарти и пишем:
    {foreach key=cid item=con from=$contacts}
    <a href="contact.php?contact_id={$cid}">{$con.name} - {$con.nick}</a><br />
    {/foreach}

    Ещё смешнее.
    "Дизайнер", ради которого все затевалось, падает в обморок от счастья.

    Получается, наши причины, по которым мы решили пользоваться шаблонами, гроша выеденного не стоят. И что, не нужны, выходит, шабоны вообще? Нужны. Но сначала надо ответить себе на вопрос - зачем. Для чего нужны шаблоны. И проверить ответ практикой. Я много раз задавал людям этот вопрос. Но почти никто не может на него ответить. Зачем ему нужны шаблоны. Получается, люди делают что-то, не зная зачем.
    Это - самое смешное.

    За время своей деятельности в качестве веб-программиста я сформулировал для себя три причины, по которым нужны шаблоны лично мне. По сути, их две. А сводятся, в конечном счете, к одной:

    Один код - несколько представлений .

    Часто бывает так, что вместо одной информации надо показать другую. Например, код получения текста новости из базы получает вместо него сообщение об ошибке. Вместо страницы с текстом новости нужно показать совсем другую - с извинениями и просбой зайти позже. С помощью шаблонов это делается элементарно.

    Часто одну и ту же информацию надо показывать в нескольких видах. К примеру - обычная страница и страница для печати. Информация та же самая, код её получения один и тот же, а код вывода - разный. Столкнувшись с такой ситуацией, очень быстро разделишь свой код на две части, одна из которых отвечает за вывод, а вторая - не отвечает.

    В чем-то похожая ситуация: допустим, наш скрипт стоит на двух сайтах. И у нас дома. И вот дома мы нашли крупную багу. Заделали её. Теперь надо обновить код на сайтах. И вот он - момент истины: если шаблоны были использованы правильно, то мы просто заливаем код на оба сайта, и все продолжает работать, как ни в чем не бывало! Такая ситуация, как я считаю - идеальная проверка выбранного подхода к шаблонизации.

    У вас могут быть свои причины использовать шаблоны. Но только с одним условием - эти причины должны быть вызваны реальной, жизненной необходимостью, а не "высшими соображениями" и заботой о каких-то неизвестных вам людях.

    В начало раздела Наверх