Archive for Жовтень 2009

PHP-кодеру на пам’ять. Випуск 2

Можливі проблеми з UTF-8

Якщо в проекті використовуються файли з кодировкою Юнікод, то можлива наступна проблема:
Warning: Cannot modify header information – headers already sent by (output started at…
Якщо все перевірено, ніде немає зайвих пробілів і т.д., а проблема збереглась, то це може означати, що файл був збережений не в “чистому” UTF-8, а в UTF-8 з сигнатурою(BOM). Деякі текстові редактори уміють зберігати файли в обох варіантах – наприклад Scite або Notepad2. Потрібно просто відкрити і зберегти файл в потрібній кодировці.
Через UTF-8 з сигнатурою(BOM) можлива також поява порожнього рядку “нізвідки” при виводі HTML через PHP.

foreach – деякі особливості

Щоб вивести певні результати за допомогою цього циклу можливі кілька варіантів. А от щоб ЗМІНИТИ САМ МАСИВ допустимий лише один шлях:
Працює:

foreach($rows as $k=>$v){
$rows[$k]['news_date'] = date('r', strtotime($rows[$k]['news_date']));
}

Не працює (оскільки ми змінюємо змінну зі значенням, а не сам масив):

foreach($rows as $k=>$v){
$v['news_date'] = date('r', strtotime($v['news_date']));
}

Змінні в PHP

Наштовхнувся в одному блозі на цікавий пост присвячений двом змінним в РНР:
DIRECTORY_SEPARATOR і PATH_SEPARATOR.

DIRECTORY_SEPARATOR – як випливає з назви, зберігає розділювач між папками, який різниться в залежності від ОС (’/'- Unix, ‘\’ – Windows).
Незважаючи на те, що Windows зрозуміє вас навіть, якщо розділяти папки символом ‘/’, всеодно коректніше використовувати DIRECTORY_SEPARATOR

PATH_SEPARATOR - зберігає розділювач для параметру РНР include_path (всі шляхи, по яким ваші скрипти шукають бібліотеки для підключкення)
Так ось, під Windows цей розділювач виглядає як ‘;’ під Unix – ‘:’
Коли потрібно підключити якусь бібліотеку (то й же Зенд) і працювати зі скриптом під різними ОС, виникають складнощі.
Донедавна я вирішував ці складнощі “геніальним” скриптиком:

if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
ini_set('include_path', ini_get('include_path') . ';' . getcwd() . DS . '..' . DS. 'lib'  .';');//WIN
} else {
ini_set('include_path', ini_get('include_path') . ':' . getcwd() . DS . '..' . DS. 'lib'  . ':');//another OS
}

Зрозуміло, що в “цивілізованому світі” (а тепер і я) роблять по-іншому:

ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR. getcwd() . DS . '..' . DS. 'lib'  . PATH_SEPARATOR);

Правильний стиль написання коду від Zend

For files that contain only PHP code, the closing tag (”?>”) is never permitted. It is not required by PHP, and omitting it´ prevents the accidental injection of trailing white space into the response
Ось як воно… А взагалі, почитати рекомендації від Zend ніколи зайвим не буде.

Економія пам’яті в PHP

При операціях з картинками на сервері варто слідкувати за витратами пам’яті на сервері і не забувати про функцію imagedestroy. Наприклад:

$src = imagecreatefromjpeg($file_temp);
.... якийсь код
imagedestroy($src);

До речі, для тестування і моніторингу скільки саме пам’яті використовує РНР на певний момент, застосовується функція memory_get_usage() (без параметрів)

Коментарів немає

А як ви запускаєте Ruby-додатки?

Ruby… Знайомо звучить, чи не так? “Модно!”- кричать одні. “Гальмує!”- кричать другі. А треті не заморочуються і продовжують використовувати PHP. Як нескладно здогадатись, я відношусь саме до третьої категорії і з Ruby практично не знайомий. Проте використання системи баг-трекінгу Redmine все-таки змусило мене дещо прояснити для себе в цій області. Основне питання було не “як щось написати під Рубі”, а “як це щось запустити?”.
Запустити не просто так, а запустити під Windows – на даний момент я використовую саме цю операційну систему. Отже, певні факти…

На сьогоднійшній день найбільш ефективним способом використання Ruby на сервері є Phusion Passenger (також відомий як mod_rack і mod_rails) Це модуль для веб-серверів Apache та Nginx. Його принцип дії схожий на принцип дії mod_wsgi для python та mod_php для PHP. Тобто ресурси сервера виділяються динамічно. Версії під Віндовс НЕ ІСНУЄ. І автори відмовляються її створювати, оскільки рішення вийде занадто повільним. Тому цей спосіб лише для Unix-систем. (раніше пробували розробляти mod_ruby, але так і не довели до стабільності і закинули, найбільш глючний)

До цього використовували зв’язку Apache/Nginx плюс ОДНОПОТОЧНИЙ сервер Mongrel. Через однопоточність доводилось запускати кілька копій Mongrel. Що було дуже неефективно з точки зору використання ресурсів. Ось цитата:

Балансировщик + Mongrel-ы
Рельса — не может работать в несколько потоков (а MRI не умеет системные потоки, т.е. не более 100% CPU), поэтому для масштабирования запускается N-ое количество монгрелов на каждой машине. Запросы к ним приходят от балансировщика.Проверенные балансировщики: nginx, apache, lighttpd. Рекомендуется nginx как наиболее стабильный, легкий и удобный в настройке.Количество монгрелов не должно быть слишком большим, чтобы зазря не сжирать память и не тратить время процессора на планировщик задач, но и не должно быть слишком малым чтобы обеспечить необходимую пропускную способность. Как правило, количество бекендов (backend, в данном случае — монгрел) наращивают по мере необходимости и до тех пор, пока производительность не перестанет расти. Когда это случится, нужно думать об оптимизации приложения и/или устанавливать дополнительный физический сервер.

Третій спосіб полягав у інтерпретації Ruby через cgi. Тобто до Apache підключались спеціальні модулі як-то FastCGI. Теж неефективно. Цей спосіб частенько зустрічається на shared-хостингах.
В поставці Ruby є власний сервер WEBrick, але його використання є недоцільним. Він тільки для “відладочних” цілей.

Підсумки. Під Windows певний час я використовував спеціальну збірку Redmine скачану з сайту Bitnami. Це такий собі флакон з веб-сервером Апач, Mongrel, Ruby i Redmine. Глючило просто неймовірно. В кінці-кінців я відмовився від таких експериментів і встановив Redmine на лінукс-платформі з використанням Phusion Passenger…
До речі, більш детально про цю установку можна почитати в попередньому пості.

Коментарів немає

Встановлення Redmine під Linux

Вирішили використовувати систему баг-трекінгу? Багзілла не вражає, Trac на Пітоні, на Mантіс глянути страшно, а тут ще й всі Redmine розхвалюють… Ставимо! Під Лінукс!

(вважається, що Апач та MySQL вже встановлені)

(для збереження нервів всі команди подані нижче краще виконувати через sudo)

sudo apt-get install ruby irb ri rdoc ruby1.8-dev libzlib-ruby libyaml-ruby libreadline-ruby libncurses-ruby libcurses-ruby libruby libruby-extras libfcgi-ruby1.8 build-essential libopenssl-ruby libdbm-ruby libdbi-ruby libxml-ruby libxml2-dev

Скачати GEM з RubyForge (http://rubyforge.org/frs/?group_id=126) розпакувати і виконати команду
sudo ruby setup.rb

sudo gem install rails
sudo gem install rake

sudo gem install passenger
sudo passenger-install-apache2-module

Скопіювати вміст скачаного архіва з Redmine в /var/www/redmine
Налаштувати конфіг для БД(перед паролем в налаштуваннях має бути пробіл!)
В параметрах БД вказати
socket: /var/run/mysqld/mysqld.sock

rake db:migrate RAILS_ENV=”production”
rake redmine:load_default_data RAILS_ENV=”production”

Не забути видалити .htaccess з папки Public

Відредагувати конфіг Апача(/etc/apache2/apache2.conf), додавши рядки(вони копіюються з інсталятора Песенджера. Віртуал Хост прописується самостійно)
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.2.5/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.2.5
PassengerRuby /usr/bin/ruby1.8

<VirtualHost 127.0.0.2:80>
ServerName redmine.com
DocumentRoot /var/www/redmine/public
</VirtualHost>


sudo /etc/init.d/apache2 restart

P.S. Мене важко віднести до фанатів Лінукс, але якщо вже ставити Редмайн, то тільки на юнікс-систему.Чому? Вся справа в тому, що Redmine написана на Ruby on Rails, а для цієї технології не існує по справжньому ефективного та надійного рішення під Windows. Трохи більше на цю тему я, можливо, розповім в одному із наступних постів.

Коментарів немає

Числа, дати, поля… – сьогодні трохи про MySQL

Бази даних: MySQL, PostgreSQL, Oracle…
- з резюме пересічного веб-программера…

Специфіка створення веб-сайтів на сьогоднішній день така, що досконально вивчати тонкощі СУБД потреби немає. Вистачає і “вершків”. Вивчив запит на вибірку, вставку, оновлення – і сміливо можна переписувати пункт “Бази даних” у власному резюме. Не те що б я це засуджував – у самого знання по MySQL поки що “базові”. Проблема в іншому: нахапавшись вершків, більшість просто перестає прагнути вчитись далі. Зрештою, в кожного свій шлях, а я хотів би навести кілька простих та корисних речей по роботі зв’язки PHP+MySQL

Робота з датою

РНР зберігає дату в форматі unix timestamp, натомість MySQL використовує формат DATETIME, тобто представлення дати у вигляді YYYY-MM-DD HH:MM:SS (2006-12-25 13:43:15)
Щоб коректно працювати з датою і часом потрібно використовувати наступні перетворення:

$mysqldate = date( ‘Y-m-d H:i:s’, $phpdate ); //Форматує системну дату/час використовуючи мітку часу, задану аргументом $phpdate
$phpdate = strtotime( $mysqldate ); //перетворює текстове представлення дати в мітку часу Юнікс

Звичайно, можна зберігати дату в БД одразу в форматі unix timestamp, використовуючи якесь числове поле, але в цьому випадку функції MySQL по роботі з часом і датою будуть недоступні.
Вивести сьoгоднішню дату в MySQL – NOW()
Вивести сьoгоднішню дату в РНР в форматі unix timestamp – strtotime(’now’) або date(’U’)

Скопіювати поле таблиці

Звучить до смішного просто, але коли я не знайшов такої операції в PHPMyAdmin і вирішив зробити це діло “вчручну”, то … виникла певна пауза :)
Отже потрібно:
ALTER TABLE `table_name` ADD `new_field` TEXT NOT NULL AFTER `some_existing field` ; – створюємо нове поле new_field після існуючого поля some_existing field
UPDATE `table_name` SET `new_field` = `some_existing field`; – копіюємо всі значення з одного поля в інше.
Щоб скопіювати всю таблицю:
create table my_table_copy (select * from my_table)
(в цьому випадку не копіюється інформація про ключові поля, необхідно ще дописувати коменду ALTER TABLE `my_table_copy` ADD PRIMARY KEY ( `my_table_copy_id` ); )
Вставити всі дані із старої таблиці в ПОРОЖНЮ нову:
insert into my_table_copy (select * from my_table)
Щоб скопіювати частину даних з однієї таблиці в іншу з умовою (офіційний мануал поспішає на допомогу):
UPDATE items,month SET items.price=month.price
WHERE items.id=month.id;

Правильне сортування чисел в текстових полях

Щоб відсортувати числа в текстовому полі коректно, а не на зразок 1,10,2 можна використовувати функцію CAST

CAST(data as UNSIGNED) AS new_data
….
ORDER BY new_data

Ще спосіб: просто написати ORDER BY data+0
Якщо ж текстове поле містить не тільки числа, а й букви, то попередньо ці букви треба видалити. Приклад:

SELECT CAST(SUBSTRING_INDEX(filename, '_', -1) as unsigned) as file, filename ,caption, photos_id as id FROM photos
WHERE o_id = '555' order by file;

По суті, у нас тут file використовується як службове поле – тільки для правильного сортування.

Коментарів немає

Сесії в PHP та куки

c2Тема ніби не з найскладніших, проте в свій час я виявив в ній багато “нового та невідомого”. В результаті з’явилась ця стаття, в котрій я спробував базово окреслити основні особливості функціонування та використання вищезгаданих технологій.

Специфіка протоколу HTTP така, що він не підтримую постійного з’єднання. Тобто “кожний раз – як вперше”. Щоб якось ідентифікувати користувача використовуються сесії та куки.

Спочатку про куки

Вперше звертаючись до сервера, браузер отримує відповідь, в якій будуть куки. Після цього всі свої звернення до сервера браузер буде супроводжувати “прикріпленням” отриманих кук.
По стандарту вони мають структуру:
Set-Cookie: RMID=732423sdfs73242; expires=Fri, 31-Dec-2010 23:59:59 GMT; path=/; domain=.example.net
Кука має ім’я RMID і значення «732423sdfs73242». Термін її зберігання сплине 31 грудня 2010 року в 23:59:59. Шлях «/» і домен «example.net» показують , що можна відкривати куку при перегляді будь-якої сторінки в домені example.net
Браузери дозволяють(на даний момент) зберігати від 30 до 50 кук з одного домена. Firefox 3 наразі зберігає куки в файлі формату SQLite (на них можна подивитись через, скажімо, Firecookie ), ІЕ 7 куки зберігає в звичайних текстових файлах.

Можливі ризики пов’язані з cookies:
1) Завдяки “стороннім” кукам, які можуть розміщуватись рекламними банерами, компанії-господарі цих банерів можуть відслідковувати куди переміщався користувач на сайтах де ці банери встановлені(вихід – можна заборонити браузеру приймати сторонні куки).
Куки може зчитати лише той сайт, якому це ДОЗВОЛЕНО в параметрах конкретно взятого кука (якщо, звичайно, в самому браузері немає “дірок”)
2)Перехоплення трафіка призведе до того, що інформація, яка міститься в куках потрапить до сторонніх людей (вихід – застосовувати https)
3)Міжсайтовий скриптинг – куки передаються на домени, на які передаватись не мали б за допомогою JavaScript і т.д.(вихід – тут уже все залежить від надійності браузера та інших факторів, наприклад – чи ввімкнута в настройках РНР опція session.cookie_httponly)

Використання в РНР:
bool setcookie ( string $name [, string $value [, int $expire [, string $path [, string $domain [, bool $secure [, bool $httponly ]]]]]] )

  • string $name – ім’я куки
  • string $value – її значення
  • int $expire – термін використання, якщо немає – то до кінця сессії
  • string $path – сторінка з яких кука буде доступна, якщо немає – то для всіх, що знаходяться в тому ж каталозі, в якому скрипт, що ВСТАНОВЛЮЄ цю куку.
  • string $domain – домен куку, якщо немає – то встановлюється поточний
  • bool $secure – якщо стоїть 1, то куки будуть передаватись на сервер тільки при HTTPS з’єднанні (але користувачу ці куки ВСТАНОВЛЮЮТЬСЯ і при звичайному з’єднанні). Наприклад: setcookie(”name”, $value, time() + 600,”",”",1);
  • bool $httponly -(з’явився лише в РНР 5.2, якщо стоїть 1, то куки стануть доступні лише через HTTP протокол (і не доступні для JS та інших скриптів). А взагалі, цей параметр можна виставити і в настройках PHP.
  • Приклад:

    setcookie(”name”, $value, time() + 600,”/web/index.php”, “.server.com”); //куки на 10 хвилин, доступні тільки з сторінки /web/index.php, яка знаходиться на одному з доменів server.com

    $_COOKIE['user_login'] – прочитати куку

    setcookie(”name”) – видалити куку

    Сесії

    Сесії – це технологія, коли дані про користувача зберігаються на сервері. Де саме? Це залежить від параметрів (в РНР – це session.save_path)
    Найчастіше – це звичайні текстові файли(сесії можуть зберігати ще й в БД) з іменем sess_281a977c051ca8c5e24b30f2a492eec2.txt Тут 281a977c051ca8c5e24b30f2a492eec2 – це ідентифікатор сесії.

    Але головна проблема – як зв’язати браузер і сервер? Зараз існують 2 шляхи:

    1) Через куки – так робиться найчастіше. Але якщо куки відключенні і параметри дозволяють, то використовується спосіб 2:
    2)Через сторінку, яка повертається браузеру, сервер сам прописує ідентифікатор сесії в лінки сторінки, в результаті отримуються ссилки виду
    site.com.ua/files/Grupa.html?PHPSESSID=6b07180cc1935abde10e4cf0527db4b1
    Цей спосіб є не таким безпечним і значно менш популярним.
    Вибір між цими двома способами в PHP здійснюється через маніпуляцію параметрами session.use_cookies,session.use_only_cookies, session.use_trans_sid

    Час життя сесії як правило 24 хвилини(цей параметр по замовчуванню, його, звичайно, можна змінювати) з моменту останньої активності користувача.

    Використання:

    session_start();
    $_SESSION['username'] = “username”;

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

    P.S. В процесі розбору з цими технологіями сильно допомогли два плагіни для Firefox: Firecookie(по суті – це плагін для Firebug) і Live HTTP headers

    Коментарів немає