Все про 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' ] - cессии работать не будут.
  • Где-то между версиями 4.2 и 5.0 невозможно было установить session.use_trans_sid с помощью ini_set () . Начиная с 5.0 уже можно снова.
  • До версии 4.3.3 куку PHP отправлял куку только если при старте сессии в запросе отсутстввал идентификатор. Теперь же кука посылается при каждом вызове 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, перечислено вверху страницы.
    Если вы знаете хороший сайт - напишите о нем в разделе "Обратная связь".

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


    \ "Лапки \". Cоставление запросов, слеши, 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__ ) 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}

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

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

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

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

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

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

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

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

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