2010-09-24

Заменяем ereg на preg_match

В PHP 5.3 разработчки решили, наконец, избавиться от POSIX регулярных выражений - в самом деле, зачем нужны две библиотеки для работы с регулярными выражениями, если можно обойтись одной? К тому же, POSIX регулярные выражения гораздо медленнее чем Perl совместимые регулярные выражения (PCRE).

Теперь, разработчики в срочном порядке исправляют свой код, заменяя функции ereg, eregi, ereg_replace, ereg_replacei соответствующими PCRE аналогами, благо, замена получилась довольно простой:

1. заменить ereg и eregi на preg_match, ereg_replace и ereg_replacei на preg_repalce;
2. добавить // разделители в начало и конец регулярного выражения;
3. если POSIX функция кончается на i (eregi, ereg_replacei) то добавить i в конец регулярного выражения после разделителя.
4. заменить POSIX спецсимволы соответствуюущими PCRE аналогами: [:alnum:] - \w или [0-9a-z], [:digit:] - \d или [0-9], [:space:] - \s, [:alpha:] - [a-z]

примеры:

ereg('expression') -> preg_match('/expression/')

eregi('expression') -> preg_match('/expression/i')

eregi('^[[:alnum:]][a-z0-9_.-]*@[a-z0-9.-]+\.[a-z]{2,4}$') -> preg_match('/^[a-z0-9][a-z0-9_.-]*@[a-z0-9.-]+\.[a-z]{2,4}$/i')

Upd: оказывается шаг четвертый не обязателен - спецсимволы типа [[:alnum:]] теперь поддерживаются в PCRE!

 

Коментариев: 68

 
  2010-08-15

Как подружить UTF-8 и PHP

При работе с UTF-8 привычные функции работы со строками перестают корректно работать. В этом не трудно убедиться если сохранять исходник страницы в UTF-8:

print strlen("тест");  //8

Вместо привычных strlen, strpos, substr следует использовать соответствующие многобайтные аналоги: mb_strlen, mb_strpos, mb_substr. Но это делает код плохо портируемым под другие кодировки, увеличивает вероятность ошибок, и вообще это не удобно. К счастью расширение mbstring позволяет переопределить эти функции автоматически.

добавляем в .htaccess

php_value mbstring.internal_encoding "UTF-8"
php_value mbstring.func_overload 7
 

проводим эксперимент:

print strlen("тест");  //4

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

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

 

Оставить комментарий

 
  2010-03-22

Полезное свойство функции substr в PHP

Функция substr служит для взятия подстроки, синткасис у нее следующий:

string substr ( string string, int start [, int length] )

Допустим, вам нужно отрезать последний символ, знатоки Javascript или Си напишут примерно следующее:

$s = 'some string1';
$s = substr($s,0,strlen($s)-1); //some string

пока терпимо, хотя код уже явно громоздкий и трудный для понимания, задача усложняется если нужно получить несколько последних символов в строке:

$s = 'some string123';
$s = substr($s,strlen($s)-4); //123

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

К счастью в PHP в substr можно использовать ОТРИЦАТЕЛЬНЫЕ ЗНАЧЕНИЯ для стартового символа и длинны:

//пример первый
$s = 'some string1';
$s = substr($s,0,-1); //some string

//пример второй
$s = 'some string123';
$s = substr($s,-3); //123

 

Кака видно, в этом случае код читается на порядок проще. Ничего подобного нет ни в Си ни в JavaScript, а жаль.

 

Коментариев: 4

 
  2010-02-03

Как сохранить изображение в ico

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

В PHPThumb обнаружил отличную функцию для сохранения GD ресурса в ICO. На основе этой функции сделал собственный мааааааленький скриптик для преобразования графических файлов в ico. В общем, инструмент получился действительно полезный - выкладываю его для общего пользования. Здесь исходник.

Поддерживает GIF, PNG и BMP.

Upd: для этих же целей есть офигительный сервис favicon.ru

Upd2: Бесплатный конкурент Фотошопа - Gimp прекрассно умеет сохранять в ico.

Upd3: Как оказалось гимп всё-же сохряняет ico с ошибкой - получается фигня, по крайней мере в Windows версии.

 

Коментариев: 16

 
  2010-01-23

PHP заменяет пробелы в именах переменных на _

Похоже, что эта пагубная традиция идет со времён когда переменные, передаваемые через GET и POST превращались в глобальные переменные. Eсли поле формы описано как <input type="text" name="Last Name"> то в PHP оно преобразуется в $_POST['Last_Name'].

Довольно неприятное поведение, но это факт.

 

Оставить комментарий

 
  2010-01-14

Локальное время в MySQL отличается от времени в PHP

Начинаю понимать почему многие разработчики забивают на тип DATETIME в MySQL и используют вместо него целочисленные поля:

  $date = gmmktime(0,0,0,1,1,2010); //2010-01-01 00:00:00;
  DB::execute("insert into messages SET date_created=from_unixtime(?)",$date);  
  $id = DB::getLastID();
  
  $r = DB::execute("select date_created from messages where message_id=?",$id);
  print $r->fields[0]; //2010-01-01 03:00:00

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

Оказывается для MySQL нужно выставлять зону отдельно.

DB::execute("SET time_zone='".Config::$mysql_timezone."'");

причем, строковые значения вида 'America/New_York' для зоны дают странные результаты - почемуто у меня получилось расхождение в 34 секунды c PHP, возможно связано с тем, что MySQL высчитывает даты с использованием leap seconds c в PHP просто прибавляет или вычитает часовую разницу, поэтому решил, что лучше использоватать числовые зоны, типа SET time_zone='-5:00'.

 

Оставить комментарий

 
  2009-12-04

Маска, маска я тебя знаю

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

Почемуто в GD нет функций для работы с масками. Пришлось изобретать самим, и вот что получилось:

Так выглядит код маскирования:

$image = imagecreatefromgif('image.gif');
$mask = imagecreatefromgif('mask.gif');

$dest = imagecreatetruecolor(imagesx($image),imagesy($image));

imagemask($dest,$image,$mask);

header('Content-type: image/jpeg');
imagepng($dest);

Код самой функции маскирования:

function imagemask($dest,$image,$mask){
	$width =  imagesx($image);
	$height = imagesy($image);
	
	$tc = imagecolorallocate($dest,0,0,0);
	imagecolortransparent($dest,$tc);
	
	for($i=0;$i<$width;$i++){
		for($j=0;$j<$height;$j++){
			$c = imagecolorat($image,$i,$j);
			$color = imagecolorsforindex($image,$c);
			
			$c = imagecolorat($mask,$i,$j);
			$mcolor = imagecolorsforindex($mask,$c);
			
			if (!($mcolor['red']==255 && $mcolor['green']==255 && $mcolor['blue']==255)){
				$c = imagecolorallocate($dest,$color['red'],$color['green'],$color['blue']);
				imagesetpixel($dest,$i,$j,$c);
			} 
		}
	}
}

Правда расстраивает, что GD так и не научилась работать нормально с полупрозрачностью - если кто знает способ, расскажите плиз.

 

Коментариев: 1

 
  2009-06-05

Как получить список координат из Google Maps

Здесь можно найти исходники и примеры на эту тему.

 

Оставить комментарий

 
  2009-04-16

Как узнать IP посетителя сайта

Казалось бы, нет ничего проще - ip адрес посетителя заносится Апачем в переменную $_SERVER['REMOTE_ADDR'] но есть некоторые нюансы...

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

Все вышесказанное учитывается в вот какой несложной функции:

function get_ip(){
    $alt_ip = $_SERVER['REMOTE_ADDR'];
    if (isset($_SERVER['HTTP_CLIENT_IP'])){
        $alt_ip = $_SERVER['HTTP_CLIENT_IP'];
    }
    else if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)){
        // make sure we dont pick up an internal IP defined by RFC1918
        foreach ($matches[0] AS $ip){
            if (!preg_match("#^(10|172\.16|192\.168)\.#", $ip)){
                $alt_ip = $ip;
                break;
            }
        }
    }
    else if (isset($_SERVER['HTTP_FROM'])){
        $alt_ip = $_SERVER['HTTP_FROM'];
    }
    return $alt_ip;
}

 

 

Коментариев: 3

 
  2009-03-20

Прелоадер-Генератор

Bomfunk выложил сабж а я его немного отрихтовал - так стало гораздо симпатичнее :)

 

Коментариев: 6