Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

PHP. Заметки. Как записать данные в начало файла


Довольно популярный вопрос среди начинающих программистов: "А как записать данные не в конец, а в начало файла?".

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

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

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

Суть ошибки заключается в том, что программист использует для временного сохранения данных оперативную память: читает файл в память, дописывает к нему новые данные, благо оперативка позволяет дописывать данные куда угодно, после чего сохранят данные в старый файл. "Чем же плох такой метод?", -- спросит любой из вас. А вот чем. Попробуйте мысленно растянуть во времени всю эту процедуру: вот данные прочтены в оперативную память... вот вы закрыли файл для чтения... вновь открыли его, но уже для перезаписи... [fatal error] ...вот тут у вас по какой-то причине оборвалось выполнение скрипта. Сбой в питании, перезагрузка, не хватило квоты на диске или случилась любая другая ошибка. Экземпляр скрипта уничтожается вместе с прочтенными в оперативку данными, а открытый для перезаписи файл автоматически закрывается девственно чистым или с некоторой частью данных, которые успели записаться. Все, данные теперь можно вытащить только из backup. Хорошо, если он у вас окажется актуальным.

Что же произошло, неужели ошибка в алгоритме? Нет, никакой ошибки в алгоритме нет. Но давайте все по порядку...

Предположим, что аварийного завершения работы скрипта у вас никогда не произойдет, хотя я в это и не верю, но пройдет какое-то время и файл с данными (предположим, что это гостевая книга) раздуется до такого размера, что просто перестанет влезать в отведенную для данного скрипта оперативную память. Вы полагаете, что можете распоряжаетесь всей свободной оперативной памятью сервера? Ничего подобного! Каждому скрипту обычно выделяется какой-то фиксированный объем памяти, которым он может распоряжаться. Как правило, это несколько мегабайт. Надеюсь, дальше эту проблему можно не описывать.

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

Все дело в том, что описанные проблемы возникают далеко не сразу. Они могут вообще не возникнуть, но где гарантия, что это будет именно так, как вы хотите?

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

"Как же так", — продолжите удивляться вы, — "а зачем тогда в PHP существуют операторы, позволяющие прочесть файл в массив строк и т. п.? Зачем они тогда существуют?". А я вам с удовольствием отвечу на этот вопрос. Существуют они для работы с файлами более-менее фиксированного объема. Самым удобным примером тут может быть файл настроек вашей программы. Конфигурационные файлы обычно имеют более-менее фиксированный размер, и сроки в них представляют собой или директивы или комментарии. Такой файл очень удобно читать в строковый массив, производить над ним действия и записывать данные обратно в файл. Вот так.

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

$file_gb="gb. txt"; // файл гостевой книги

$file_tmp="gb_tmp. txt"; // временный файл

$str="новая строка текста";

// проверяем, не было ли сбоя в предыдущем запуске скрипта

if(file_exists($file_tmp)) die("fatal error, call administrator!");

// копируем содержимое файла в tmp

if(copy($file_gb, $file_tmp))

{

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

if($w=fopen($file_gb,"w"))

{

flock($w,2); // локируем файл

fwrite($w,$str."\n"); // записываем первую строку

if(!$r=fopen($file_tmp,"r")) die("can't open file");

flock($r,1);

while($str=fgets($r,10240)) // читаем построчно

{

fputs($w,$str); // пишем построчно

}

flock($r,3);

fclose($r);

flock($w,3);

fclose($w);

unlink($file_tmp);

}

}


Вот так. А теперь попробуйте представить себе ситуацию, когда данные могут быть испорчены работой этого скрипта. И не пытайтесь! На каком этапе работы не произошел бы сбой, всегда сохранится актуальная информация: или в основном файле, или во временном. Причем, если информация осталась во временном файле, то следующий экземпляр скрипта не станет работать, чтобы не затереть данные в tmp-файле.

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

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

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

Желаю удачи!

PHP. Заметки. Захват файлов flock()

НЕ нашли? Не то? Что вы ищете?


Как правило, в качестве хранения информации для web-сайтов используют файлы (их еще называют "текстовыми файлами") и SQL-таблицы. Гораздо реже используют таблицы Berkeley DB (DBM) и другие форматы хранения информации.

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

В PHP существует крайне полезная функция flock(), которая в состоянии обезопасить ваши текстовые файлы от возможного повреждения данных в момент конкурентного (одновременного) обращения к ним. К моему великому сожалению, данную функцию почти всегда опускают в скриптах, полагаясь на современную скорость записи на диск. Чаще всего это срабатывает, но не до бесконечности. Рано или поздно наступает момент, когда два пользователя одновременно нажимают кнопку "сохранить", и два экземпляра одного и того же скрипта пытаются одновременно произвести две записи в один и тот же файл. Например, это могут быть две записи в гостевую книгу сайта. Результат — непредсказуем: от неудачного сохранения новой записи до краха всего файла гостевой книги.

А обезопасить себя от подобных ошибок — пара пустяков.

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

На примере это выглядит так. Предположим, нам нужен скрипт, который дописывает в конец файла example. txt содержимое переменной $str. Что пишут в подобном случае:

if(!$w=fopen("example. txt","a+")) die "can't open file for append...";

fwrite($w,$str."\n");

fclose($w);


А так это должно быть написано с использованием захвата файлов:

if(!$w=fopen("example. txt","a+")) die "can't open file for append...";

flock($w,2);

fwrite($w,$str."\n");

flock($w,3);

fclose($w);


Вот этот скрипт уже будет работать с файлом безопасно. И если несколько экземпляров данного скрипта захотят произвести запись в файл, то они сделают это по очереди, не толкаясь и не пихая все в одну кучу.

Согласитесь, не слишком большие затраты для обеспечения безопасности ваших данных.

Теперь о циферках, которые указываются в параметрах flock(), вместе с указателем на файл.

1 — захват файла для чтения. Этот вид захвата говорит другим программам, что файл в данный момент читается. Этот захват не считается "монопольным", и другие программы в лобой момент могут начать чтение данного файла. А если необходимо сделать запись в файл, то fopen() сначала дождется своей очереди, после чего откроет файл для записи:

2 — захват файла для записи. Этот вид захвата называется "монопольным", т. к. доступ к файлу всем другим программам запрещен.

3 — освобождение файла. Вызов flock с параметром "3" освобождает файл от захвата. Не забывайте вызывать flock() с этим параметром, когда закончили работать с файлом.

Следует помнить, что функция flock() возымеет действие на файл только в том случае, если она применяется к открытому файлу. На закрытый файл данная функция наложить захват не может. Другими словами, функция flock() с параметром 1 или 2 должна вызываться после открытия файла (fopen()), а flock() с параметром 3 следует вызывать перед его закрытием (fclose()).

Более точное описание функции flock() вы можете найти на странице рук-ва: http://www. /manual/en/function. flock. php.

pentek_imre at mailbox dot hu
31-Oct-2004 06:51

flock isn't good in race conditions. I accept that it can correctly lock file and can correctly block php processes if file is locked, but anyway this function isn't the right way to manage a race condition.
Let's have a look at this code:
<?php
$f=fopen($filename,"a+") or die();
flock($f, LOCK_EX) or die();
//here write some lines to the file -- not included
//then close:
flock($f, LOCK_UN) or die();
fclose($f) or die();
?>
Then generate a race situation with two php processes:
1: open file ok, no file found, create
2: open file ok, file found seek to the end (0lenght file so to the beginning)
1: lock file ok
2: flock waits since file is already locked.
1: write ok
1: unlock ok
2: flock ok this process now continues
1: fclose ok
2: write something, but due to prebuffering the file is now empty, so content written by 1 is now unconsidered, forgotten.
2: unlock ok
2: fclose ok
file will have only the content from process 2
this is the situation if you use r+ too, no matter if you used fflush or not.
conclusion: you may want to create a separate lock file!
separate lock file will behave like in the previous example too, so let's try LOCK_NB and $wouldblock, file close and reopen.
let's see this example:
<?php
$file="/tmp/phplockfile";
do
 {
  if(isset($f))
  {
  flock($f, LOCK_UN);
  fclose($f);
  }
  $f=fopen($file,"a") or die();
  flock($f, LOCK_EX+LOCK_NB,$W);
//  sleep(1);
 }
while ($W==1);

//lock is mine:
echo $_SERVER["UNIQUE_ID"]." ".date("r")."\n";
sleep(1);
echo $_SERVER["UNIQUE_ID"]." ".date("r")."\n";

//release the lock:
flock($f, LOCK_UN);
fclose($f);
?>
I tried this code for 10 (ten) parellel processes. only three of them succeeds to lock the file and unlock it, the other seven quits with execuition timeout. uncommenting the sleep(1); won't help too, just execution will be longer (30 sec is counted not as real time but as cpu time)
I tried random usleep too, as I remember this wasn't helped too.
Remember that file close and reopen is a must becouse processes may write to the file, and this way these extra bytes will be considered too.

rehfeld. us
28-Sep-2004 07:04

<?php

/*
 * I hope this is usefull.
 * If mkdir() is atomic,
 * then we do not need to worry about race conditions while trying to make the lockDir,
 * unless of course were writing to NFS, for which this function will be useless.
 * so thats why i pulled out the usleep(rand()) peice from the last version
 *
 * Again, its important to tailor some of the parameters to ones indivdual usage
 * I set the default $timeLimit to 3/10th's of a second (maximum time allowed to achieve a lock),
 * but if your writing some extrememly large files, and/or your server is very slow, you may need to increase it.
 * Obviously, the $staleAge of the lock directory will be important to consider as well if the writing operations might take  a while.
 * My defaults are extrememly general and you're encouraged to set your own
 *
 * $timeLimit is in microseconds
 * $staleAge is in seconds
 *
 *
 */

function microtime_float()
{
  list($usec, $sec) = explode(' ', microtime());
  return ((float)$usec + (float)$sec);
}

function locked_filewrite($filename, $data, $timeLimit = $staleAge = 5)
{
  ignore_user_abort(1);
  $lockDir = $filename . '.lock';

  if (is_dir($lockDir)) {
      if ((time() - filemtime($lockDir)) > $staleAge) {
          rmdir($lockDir);
      }
  }

  $locked = @mkdir($lockDir);

  if ($locked === false) {
      $timeStart = microtime_float();
      do {
          if ((microtime_float() - $timeStart) > $timeLimit) break;
          $locked = @mkdir($lockDir);
      } while ($locked === false);
  }

  $success = false;

  if ($locked === true) {
      $fp = @fopen($filename, 'a');
      if (@fwrite($fp, $data)) $success = true;
      @fclose($fp);
      rmdir($lockDir);
  }

  ignore_user_abort(0);
  return $success;
}

?>

jeroenl at see dot below
07-Jul-2004 08:16

lockwang, thanx for pointing out the bug I forgot to update overhere.

I agree (as mentioned) that an old not unlinked lockfile will stop writing, but at least writing will never corrupt old data and if needed you can add a lock clean up.
If you want uncorrupted files and a possible data loss is not a very big problem (like with traffic logging, etc.) this solution does work.

After trying a lot of things (always corrupting files) I use this code now since a few months for loggings and counters on many sites, resulting in (gzipped) files up to 2 Mb, without any corrcupted file or any not unlinked lockfile yet (knock knock...).

A better solution (100% save would be nice :) would be great, but since I haven't seen one around yet...

If more is needed SQLite (if available) might be an alternative.

Corrected code:
<?
  function fileWrite($file, &$str, $lockfile = null) {
      // ceate a lock file - if not possible someone else is active so wait (a while)
      $lock = ($lockfile)? $lockfile : $file.'.lock';
      $lf = @fopen ($lock, 'x');
      while ($lf === FALSE && $i++ < 20) {
          clearstatcache();
          usleep(rand(5,85));
          $lf = @fopen ($lock, 'x');
      }
      // if lockfile (finally) has been created, file is ours, else give up...
      if ($lf!== False) {
          $fp = fopen( $file, 'a');
          fputs( $fp, $str); // or use a callback
          fclose( $fp);
          // and unlock
          fclose($lf);
          unlink($lock);
      }
  }
?>

cia pataisymas

If you don't want use secondary lock file while truncating, try this:

<?php

$fp = fopen("/tmp/lock. txt", "a+");

if (flock($fp, LOCK_EX)) { // do an exclusive lock
  ftruncate($fp, 0);
  fwrite($fp, "Write something here\n");
  flock($fp, LOCK_UN); // release the lock
} else {
  echo "Couldn't lock the file!";
}

fclose($fp);

?>

PHP. Заметки. Краткая запись выражений - 1


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

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

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

Начнем с самого простого.

Выражение $a=$a+1 — хорошо известно как $a++ или ++$a, но можно еще написать $a+=5 вместо $a=$a+5. Все то же самое касается и вычитания. А если вы складываете строки: $str=$str."substr", то выражение может выглядеть как $a.="substr".

* * *



Как часто вам приходится включать вывод PHP-переменных в html-текстах? Да постоянно, я думаю. Знакомое выражение, правда?

<img src=/<? echo $path; ?> border=<? echo $border; ?> alt="<? echo $alt; ?>">


Если знакомо, то предлагаю такой вариант:

<img src=/<?=$path?> border=<?=$border?> alt="<?=$alt?>">


PHP-команд почти не видно, от чего код становится удобнее к прочтению. А для тех, кто медленно печатает — еще и экономия времени.

* * *



А вы знаете, что при использовании ассоциативных массивов не обязательно использовать кавычки или апострофы для ограничения ключа ячейки?

echo $arr['mama']=$arr['papa'] равносильно echo $arr[mama]=$arr[papa].

Но надо быть осторожным с ключами, которые содержат в себе символы, запрещенные в именах переменных. Если в ключе может проскользнуть пробел, знак $ или еще что-то запрещенное, то лучше использовать кавычки.

Еще надо помнить о константах. Если mama или papa окажется объявленной константой, то будет использоваться значение ячейки с ключом — содержимым константы.

* * *



Довольно часто приходится проверять переменную на существование, на отличие от нуля или просто на булевское "правда или ложь".

Стандартные формы проверки выглядят так:

if($a!=0) — это отличие от нуля

if($a==true) — это проверка на "правду"

if(isset($a)) — это проверка на существование переменной

Но можно использовать и такое выражение:

if($a) — аналог всех трех приведенных выражений вместе взятых. Условие "сработает", если $a — существующая переменная, которая несет в себе данные, отличные от нуля, либо булевское "true".

Соответственно, обратное условие будет выглядеть как if(!$a).

PHP. Заметки. Контроль над раздачей файлов


Рано или поздно в жизни каждого web-программиста появляется задача — контролировать раздачу файлов пользователям.

Наиболее распространенная ситуация — это раздача музыки в формате mp3 и архивов программ.

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

Сам по себе подсчет сумм размеров запрошенных файлов не представляет никакой сложности. Для этого достаточно сложить размеры файлов и сделать соответствующие выводы в "кошельке" пользователя услугой. Но как быть, если файл реально не был получен пользователем до конца? К сожалению, такое случается и довольно часто, ибо в на территории бывшего СССР телефонная связь оставляет желать лучшего.

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

Не смотря на кажущуюся сложность, задача имеет довольно простое решение. Нам поможет функция PHP readfile().

Функция readfile() делает простейшую операцию — читает данные из файла и направляет их в буфер исходящего потока данных (output buffer). То есть, в нашем случае, просто читает байты из файла и передает их пользователю. Но! После того, как операция успешно завершена или прервана, функция readfile() возвращает количество успешно прочитанных байтов, что, собственно, нам и требуется.

Если мы знаем точный размер отдаваемого файла и знаем количество прочтенных из него байтов, мы сразу можем сделать два нужных нам вывода: был ли файл прочитан до конца (т. е. пользователь его получил), и сколько всего байтов было передано пользователю.

Однако, чтобы правильно реализовать контроль над отдачей файла, необходимы некоторые условия работы PHP-скрипта. Дело в следующем.

Порядок выполнения подобного скрипта очевиден:

1. Определяем размер и доступность файла
2. Отправляем нужные http-заголовки (headers)
3. Читаем файл функцией readfile()
4. Получаем объем переданных данных
5. Анализируем результат передачи данных
6. Фиксируем выводы

Проблемы выполнения скрипта могут начаться на стадии выполнения пункта 3. Основных проблем я вижу две:

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

2. В процессе передачи разорвется соединение, и скрипт "умрет" сам по себе, не доходя до выполнения пункта 4. То есть мы не зафиксируем объем переданных данных, если файл не был удачно прочитан до конца.

Эти две проблемы решаются двумя директивами интерпретатору, поставленными в начало скрипта:

set_time_limit(0);

ignore_user_abort(1);

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

Разумеется, в первой директиве можно указать не 0, а максимальное количество секунд, отведенное на работу скрипта.

Главное, чтобы сборка и настройка PHP-движка дозволяла использовать данные директивы.

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

PHP. Заметки. connect или pconnect?


Сегодня PHP предоставляет нам выбор между двумя типами подключений к базам данных: обычное подключение и так называемое постоянное подключение (persistent connection).

Хоть это и относится к различным SQL-базам, но разницы в подходе нет почти никакой; по обыкновению, мы будем рассматривать пример с MySQL.

В последнее время тема постоянных соединений довольно часто поднимается на моем форуме (http://*****), хочу, как говорится, внести некоторую ясность в этот вопрос.

Принципиальное отличие традиционного соединения от постоянного заключается в самом названии — "постоянное" соединение.

Как же так? Ведь соединение должно закрыться после того, как PHP-скрипт завершил свою работу. А вот и нет. При вызове функции pconnect PHP-движок совершает следующие действия: проверяет, нет ли уже установленного соединения к указанной базе данных указанным пользователем; если соединение есть, то идентификатор соединения просто передается выполняющемуся скрипту; если указанного соединения нет, то оно создается и идентификатор опять же передается скрипту.

После завершения работы скрипта PHP не закрывает соединение, а продолжает его сохранять, раздавая указатели на него другим скриптам, и даже если ваш скрипт завершается вызовом mysql_close(), это ничуть не помешает PHP сохранить persistent connection.

Когда же на самом деле "умрет" соединение? Конечно, соединение не может жить само по себе, оно живет в оперативном пространстве работающего экземпляра (child) apache. И как только child перестает существовать, с ним "умирает" и постоянное соединение.

Каковы же минусы и плюсы постоянного подключения к базе данных?

Минусов, скажу я вам, практически нет. Просто надо быть осторожным, чтобы количество "детей" apache не превышало параметра max_user_connection в настройках MySQL. Дело в том, что каждый экземпляр apache работает независимо от других, поэтому создает свое собственное постоянное соединение, и если количество "детей" apache (а вместе с ним и количество постоянных соединений) превысит максимально допустимое значение MySQL, возникнет ошибка подключения к базе данных. Чтобы подобных ошибок не происходило, убедитесь, что параметр apache MaxClient не превышал параметра MySQL max_user_connection. А заодно следите за тем, чтобы в ваших скриптах не смешивались постоянные и обычные "короткие" подключения к базе данных. Это тоже может вызвать переполнение допустимого значения одновременных подключений.

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

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

28.01.09

PHP. Заметки. Эмуляция директивы register_globals on


Есть в PHP такая интересная директива, под названием register_globals, определенная в php. ini. Директива указывает компилятору, что значения входящих (глобальных) переменных следует изъять из их системных массивов и представить в виде самостоятельных переменных. К таким данным относится все, что передается в скрипт "снаружи": данные из форм, данные из URL, cookie и так далее. Лично мне эта директива нравится, ибо она экономит время написания скриптов и делает их более читаемыми. Сравните сами, что выглядит приятнее и удобнее для глаза:

Здравствуйте, уважаемый <?=$_COOKIE['username']>>, мы рады вам!

Здравствуйте, уважаемый <?=$username?>, мы рады вам!

Вообще, лично я люблю PHP в основном за его удобочитаемость и быстрое написание.

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

Эта дыра, которую записали в разряд "дыр в PHP" лично я считаю дырой в программировании, а не в языке, вынудила разработчиков PHP рекомендовать отключение register_globals. Что тут же было воспринято общественность как руководство к действию: все стали выключать регистрацию глобальных переменных на своих серверах.

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

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

Насколько я понимаю, подобная задача, в конце концов, сводится к тому, чтобы в начале работы скрипта присвоить привычным переменным их значения из глобальных массивов $_REQUEST, $_POST, $_GET, $_COOKIE, $_SERVER и т. д.

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

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

Если вы решили перенести все свои проекты на новый хостинг, а провайдер запрещает использовать регистрацию глобальных переменных — у вас проблемы.

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

Я предлагаю следующее решение. Надо просто эмулировать работу register_globals в одном отдельно взятом скрипте или в начале объектно-ориентированной стркутуры.

Как это сделать. Да не очень уж и сложно. Давайте рассуждать логически: имена переменных и их значения содержатся в соответствующих глобальных массивах. Как правило, используется массив $_REQUEST, который объединяет в себе все переменных GET, POST и COOKIE. Т. е. все, что передается скрипту из браузера, то, с чем работают скрипты.

Значит, надо извлечь из массива имена переменных, значения переменных и присвоить первому — второе.

Извлечь — не проблема, для этого подойдет функция перебора всех ячеек массива foreach(), но как присвоить? Если у нас в $_REQUEST[username] содержится "atos", то как программно создать переменную $username со значением "atos"? Мы же не можем заранее знать, какие имена переменных будут в массиве $_REQUEST.

Вот тут нам поможет изумительная функция eval(), которая редко используется в обычном программировании, но буквально незаменима в некоторых случаях. О самой функции стоит написать отдельно, поэтому скажу только пару слов для тех, кто с ней не знаком.

Функция eval() заставляет PHP рассматривать обыкновенный текст, содержащийся в переменной, как фрагмент PHP-кода. Говоря языком примеров, результаты работы строк

echo 'Hello, User!';

и

eval("echo \'Hello, User!\';")

будет идентичными.

Вот eval() и поможет нам объявить все переменные из массива $_REQUEST. Выглядит это совсем коротко:

foreach($_REQUEST as $k=>$v)

{

eval("\$$k='$v';");

}

Вставьте этот цикл в самом начале вашего скрипта; он переберет массив глобальных переменных и объявит их не хуже register_globals. А может быть даже и лучше, т. к. глобальных массивов много, а вытаскивать переменные не обязательно из всех. Как правило, данных из массива $_REQUEST — вполне достаточно.

Однако, не стоит забывать и о безопасности вашего кода. Обратите внимание на специфику работы функции eval() — она обработает весь код, переданный ей в качестве параметра. Будьте осторожны, примите меры безопастности, чтобы злоумышленник не подсунул в качестве названия или содержимого переменной кусок своего php-кода или просто неверные данные, способные вызвать ошибку (например, имя переменной, начинающее с цифры или другого неразрешенного символа).

09.02.2003


P.S. После опубликования этой заметки, на форуме не один раз высказывались мысли о том, что вариант с eval() — не самый лучший. Лично я предпочитаю оставлять для себя "путь к отступлению", и eval() как раз привлекателен тем, что позволяет полностью контролировать процесс, добавляя необходимые проверки и ограничения в процедуру или наоборот — расширяя возможности кода. Например, можно добавить логирование регистрации отдельных переменных, чтобы знать — кто, куда, откуда, зачем, или четко запретить к регистрации определенные имена переменных или другие данные.

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

Метод "переменные переменных".

foreach($_REQUEST as $k=>$v)

{

$$k=$v;

}

Как видите, тоже простой метод, основанный на том, что значение переменной $k используется в качестве имени новой переменной. Удобно. Отличается автоматической регистрацией не только переменных, но и массивов. В случае с методом eval() придется проверять каждую переменную на is_array() и разворачивать (регистрировать) ее дополнительно, если такой массив вам нужен.

И самый простой метод — extract().

Весь код нашего примера будет выглядеть так:

extract($_REQUEST);

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

И, наконец, не забывайте о том, что register_globals можно активировать не только в конфигурационном файле Apache, но и в файле. htaccess вашего сайта.

Удачи вам, и не пишите дырявых скриптов!

06.03.2003

PHP. Борьба с примитивным flood-ом


Некоторое время назад передо мной встала проблема безопасности несколько острее, чем стояла до сих пор. Дело в том, что мне пришлось программировать проект, который уже много лет было модно взламывать. "Ко01}{ацкеров" развелось достаточно много и они беспрестанно думают, что бы такого сделать плохого. Путей насолить обычно бывает огромное множество, программисту практически не реально поставить стопроцентно надежный заслон на каждом из них. Но к этому нужно стремиться. Я хочу обсудить пока один из аспектов защиты. Речь пойдет о защите от flood-атак, от одной из их разновидностей.

Flood - это атака, при которой атакующий заваливает сервер частыми запросами.

Разновидностей таких атак существует огромное множество, бывают атаки на уровне TCP/IP, бывают на уровне прикладных протоколов типа HTTP, FTP и т. п. Бывают атаки распределенные, когда атака производится с множества адресов, с такими атаками очень сложно бороться; бывали случаи, когда такими атаками доводили до банкротства вполне серьезных и крупных провайдеров.

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

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

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

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

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

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

// проверка на превышение частоты обращения к скрипту с одного IP-адреса

// (c) Oleg V. Konstantinov 2003.

// syntax: $res = freq_limit($filename,$ip,$interval,$limit);

// $filename имя файла для хранения

// $ip адрес текущего запроса

// $interval интервал ограничения

// $limit максимальное кол-во обращений за интервал

// $res кол-во запросов, если есть превышение частоты, иначе 0;

function freq_limit($filename,$ip,$interval,$limit){

$lip = ip2long($ip);

$tmp = array(); // массив для временного хранения

$requests = 1; // счетчик запросов с данного IP в течении интервала,

// 1 - учитываем текущий запрос

//читаем файл, открывая его для последующей записи

$in = fopen($filename,"r+");

if($in){

flock($in, LOCK_EX ) or die("Cannot flock file.");

$now = time();

while($block = fread($in,8)){

//каждая запись - ip, time в двоичном формате

$arr = unpack("Lip/Ltime",$block);

//если обращение прокисло(прошло время больше интервала) игнорируем

if( ($now - $arr['time']) > $interval ){

continue;

}

// если в течении интервала был запрос с текущего IP, увеличиваем счетчик

if($arr['ip'] == $lip){

$requests++;

}

$tmp[] = $arr;

}

//обнуляем файл, для последующей записи

fseek($in,0);

ftruncate($in,0);

}else{ //если нет файла, создаем его

$in = fopen($filename,"w");

flock($in, LOCK_EX ) or die("Cannot flock file.");

}

//заливаем массив

for($i=0;$i < count($tmp);$i++){

fwrite($in, pack('LL',$tmp[$i]['ip'],$tmp[$i]['time']));

}

//записываем текущий запрос

fwrite($in, pack('LL',$lip,$now));

// закрываем файл, блокировка снимается автоматически

fclose($in);

// если лимит превышен возвращаем кол-во обращений, иначе 0

return ($reqs > $limit)?$reqs:0;

}

// Собственно скрипт. Разрешает выполнять с одного IP-адреса до 5 запросов

// в минуту.

if(freq_limit("freq-req. bin", getenv("REMOTE_ADDR"),60,5)){

echo "Абламайтес!";

exit;

}

echo "Усе пучком!";

PHP. Заметки. Запись строковых выражений, кавычки


С большим удивлением узнал, что многие (иногда даже не начинающие) PHP-программисты не знакомы с разницей между обработкой данных в кавычках (") и апострофах ('). А между тем, разница очень важна, хоть и проста по своей сути — в кавычках данные "парсятся", а в апострофах — нет.

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

В общем, не надо быть семи пядей во лбу, чтобы сделать вывод — строки в одинарных кавычках обрабатываются быстрее, чем в двойных. И тесты показывают, что так оно и есть.

Подробнее о строках можно прочесть на странице руководства по PHP: http://www. /manual/en/language. types. string. php.

Пожалуй, получилась самая короткая PHP-заметка. Давайте дополним ее некоторыми примерами записи строк в PHP.

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

$query="select $field from $table where $field='$data' limit $n";

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

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

Следующим шагом навстречу PHP можно назвать вынесением имен переменных из строки:

$query="select ".$field." from ".$table." where ".$field."='".$data."' limit ".$n;

Это ускорит обработку строки, можете мне поверить.

Что можно сделать еще? Нужно поменять двойные кавычки на одинарные апострофы, чтобы PHP не пытался разбирать строки в поисках переменных и спецсимволов:

$query='select '.$field.' from '.$table.' where '.$field.'=\''.$data.'\' limit '.$n;

Вот мы и привели строку к "правильному" виду. В таком виде строка будет понята интерпретатором быстро и без разночтений.

Напоследок попробую выдумать забавный пример комбинирования одинарных и двойных кавычек:

echo 'Переменная $name содержит имя \''.$name.'\'.<br>'."\n".'А переменная $id содержит цифру '.$id.', которая в свою очередь поможет выбрать из массива $select[] значения:<br>'."\n".'<pre>name'."\t".'age'."\t".'town</pre> и другие...'."\n".'.';

Рузультат работы этой строки выглядит так:

-----
Переменная $name содержит имя ''.
А переменная $id содержит цифру, которая в свою очередь поможет выбрать из массива $select[] значения:

name age town

и другие... .
-----

Но и это еще не все. Если речь идет о html-файле, то последний пример можно записать, например, как:

-----
<html><body>
Переменная $name содержит имя '<?=$name?>'.<br>
А переменная $id содержит цифру <?=$id?>, которая в свою очередь поможет выбрать из массива $select[] значения:<br>
<pre>
name age town
</pre>
и другие...<br>
</body></html>
-----

Результат будет таким же, только выполнится скрипт гораздо быстрее. Можно предложить и еще несколько вариантов записи этого выражения, но я завязываю с этим увлекательным занятием.

06.03.03


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

Strings

A string is series of characters. In PHP, a character is the same as a byte, that is, there are exactly 256 different characters possible. This also implies that PHP has no native support of Unicode. See utf8_encode() and utf8_decode() for some Unicode support.

Note: It is no problem for a string to become very large. There is no practical bound to the size of strings imposed by PHP, so there is no reason at all to worry about long strings.

Syntax

A string literal can be specified in three different ways.

·  single quoted

·  double quoted

·  heredoc syntax

Single quoted

The easiest way to specify a simple string is to enclose it in single quotes (the character ').

To specify a literal single quote, you will need to escape it with a backslash (\), like in many other languages. If a backslash needs to occur before a single quote or at the end of the string, you need to double it. Note that if you try to escape any other character, the backslash will also be printed! So usually there is no need to escape the backslash itself.

Note: In PHP 3, a warning will be issued at the E_NOTICE level when this happens.

Note: Unlike the two other syntaxes, variables and escape sequences for special characters will not be expanded when they occur in single quoted strings.

<?php
echo 'this is a simple string';

echo 'You can also have embedded newlines in
strings this way as it is
okay to do';

// Outputs: Arnold once said: "I'll be back"
echo 'Arnold once said: "I\'ll be back"';

// Outputs: You deleted C:\*.*?
echo 'You deleted C:\\*.*?';

// Outputs: You deleted C:\*.*?
echo 'You deleted C:\*.*?';

// Outputs: This will not expand: \n a newline
echo 'This will not expand: \n a newline';

// Outputs: Variables do not $expand $either
echo 'Variables do not $expand $either';
?>

Double quoted

If the string is enclosed in double-quotes ("), PHP understands more escape sequences for special characters:

Table 11-1. Escaped characters

sequence

meaning

\n

linefeed (LF or 0x0A (10) in ASCII)

\r

carriage return (CR or 0x0D (13) in ASCII)

\t

horizontal tab (HT or 0x09 (9) in ASCII)

\\

backslash

\$

dollar sign

\"

double-quote

\[0-7]{1,3}

the sequence of characters matching the regular expression is a character in octal notation

\x[0-9A-Fa-f]{1,2}

the sequence of characters matching the regular expression is a character in hexadecimal notation

Again, if you try to escape any other character, the backslash will be printed too!

But the most important feature of double-quoted strings is the fact that variable names will be expanded. See string parsing for details.

Heredoc

Another way to delimit strings is by using heredoc syntax ("<<<"). One should provide an identifier after <<<, then the string, and then the same identifier to close the quotation.

The closing identifier must begin in the first column of the line. Also, the identifier used must follow the same naming rules as any other label in PHP: it must contain only alphanumeric characters and underscores, and must start with a non-digit character or underscore.

Warning

It is very important to note that the line with the closing identifier contains no other characters, except possibly a semicolon (;). That means especially that the identifier may not be indented, and there may not be any spaces or tabs after or before the semicolon. It's also important to realize that the first character before the closing identifier must be a newline as defined by your operating system. This is \r on Macintosh for example.

If this rule is broken and the closing identifier is not "clean" then it's not considered to be a closing identifier and PHP will continue looking for one. If in this case a proper closing identifier is not found then a parse error will result with the line number being at the end of the script.

It is not allowed to use heredoc syntax in initializing class members. Use other string syntaxes instead.

Example 11-3. Invalid example

<?php
class foo {
  public $bar = <<<EOT
bar
EOT;
}
?>

Heredoc text behaves just like a double-quoted string, without the double-quotes. This means that you do not need to escape quotes in your here docs, but you can still use the escape codes listed above. Variables are expanded, but the same care must be taken when expressing complex variables inside a heredoc as with strings.

Example 11-4. Heredoc string quoting example

<?php
$str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;

/* More complex example, with variables. */
class foo
{
  var $foo;
  var $bar;

  function foo()
  {
      $this->foo = 'Foo';
      $this->bar = array('Bar1', 'Bar2', 'Bar3');
  }
}

$foo = new foo();
$name = 'MyName';

echo <<<EOT
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should print a capital 'A': \x41
EOT;
?>

Note: Heredoc support was added in PHP 4.

Variable parsing

When a string is specified in double quotes or with heredoc, variables are parsed within it.

There are two types of syntax: a simple one and a complex one. The simple syntax is the most common and convenient. It provides a way to parse a variable, an array value, or an object property.

The complex syntax was introduced in PHP 4, and can be recognised by the curly braces surrounding the expression.

Simple syntax

If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible to form a valid variable name. Enclose the variable name in curly braces if you want to explicitly specify the end of the name.

<?php
$beer = 'Heineken';
echo "$beer's taste is great"; // works, "'" is an invalid character for varnames
echo "He drank some $beers";  // won't work, 's' is a valid character for varnames
echo "He drank some ${beer}s"; // works
echo "He drank some {$beer}s"; // works
?>

Similarly, you can also have an array index or an object property parsed. With array indices, the closing square bracket (]) marks the end of the index. For object properties the same rules apply as to simple variables, though with object properties there doesn't exist a trick like the one with variables.

<?php
// These examples are specific to using arrays inside of strings.
// When outside of a string, always quote your array string keys
// and do not use {braces} when outside of strings either.

// Let's show all errors
error_reporting(E_ALL);

$fruits = array('strawberry' => 'red', 'banana' => 'yellow');

// Works but note that this works differently outside string-quotes
echo "A banana is $fruits[banana].";

// Works
echo "A banana is {$fruits['banana']}.";

// Works but PHP looks for a constant named banana first
// as described below.
echo "A banana is {$fruits[banana]}.";

// Won't work, use braces.  This results in a parse error.
echo "A banana is $fruits['banana'].";

// Works
echo "A banana is " . $fruits['banana'] . ".";

// Works
echo "This square is $square->width meters broad.";

// Won't work. For a solution, see the complex syntax.
echo "This square is $square->width00 centimeters broad.";
?>

For anything more complex, you should use the complex syntax.

Complex (curly) syntax

This isn't called complex because the syntax is complex, but because you can include complex expressions this way.

In fact, you can include any value that is in the namespace in strings with this syntax. You simply write the expression the same way as you would outside the string, and then include it in { and }. Since you can't escape '{', this syntax will only be recognised when the $ is immediately following the {. (Use "{\$" or "\{$" to get a literal "{$"). Some examples to make it clear:

<?php
// Let's show all errors
error_reporting(E_ALL);

$great = 'fantastic';

// Won't work, outputs: This is { fantastic}
echo "This is { $great}";

// Works, outputs: This is fantastic
echo "This is {$great}";
echo "This is ${great}";

// Works
echo "This square is {$square->width}00 centimeters broad.";

// Works
echo "This works: {$arr[4][3]}";

// This is wrong for the same reason as $foo[bar] is wrong
// outside a string.  In other words, it will still work but
// because PHP first looks for a constant named foo, it will
// throw an error of level E_NOTICE (undefined constant).
echo "This is wrong: {$arr[foo][3]}";

// Works.  When using multi-dimensional arrays, always use
// braces around arrays when inside of strings
echo "This works: {$arr['foo'][3]}";

// Works.
echo "This works: " . $arr['foo'][3];

echo "You can even write {$obj->values[3]->name}";

echo "This is the value of the var named $name: {${$name}}";
?>

String access and modification by character

Characters within strings may be accessed and modified by specifying the zero-based offset of the desired character after the string in curly braces.

Note: For backwards compatibility, you can still use array-brackets for the same purpose. However, this syntax is deprecated as of PHP 4.

Example 11-5. Some string examples

<?php
// Get the first character of a string
$str = 'This is a test.';
$first = $str{0};

// Get the third character of a string
$third = $str{2};

// Get the last character of a string.
$str = 'This is still a test.';
$last = $str{strlen($str)-1};

// Modify the last character of a string
$str = 'Look at the sea';
$str{strlen($str)-1} = 'e';
       
?>

Useful functions and operators

Strings may be concatenated using the '.' (dot) operator. Note that the '+' (addition) operator will not work for this. Please see String operators for more information.

There are a lot of useful functions for string modification.

See the string functions section for general functions, the regular expression functions for advanced find&replacing (in two tastes: Perl and POSIX extended).

There are also functions for URL-strings, and functions to encrypt/decrypt strings (mcrypt and mhash).

Finally, if you still didn't find what you're looking for, see also the character type functions.

Converting to string

You can convert a value to a string using the (string) cast, or the strval() function. String conversion is automatically done in the scope of an expression for you where a string is needed. This happens when you use the echo() or print() functions, or when you compare a variable value to a string. Reading the manual sections on Types and Type Juggling will make the following clearer. See also settype().

A boolean TRUE value is converted to the string "1", the FALSE value is represented as "" (empty string). This way you can convert back and forth between boolean and string values.

An integer or a floating point number (float) is converted to a string representing the number with its digits (including the exponent part for floating point numbers).

Arrays are always converted to the string "Array", so you cannot dump out the contents of an array with echo() or print() to see what is inside them. To view one element, you'd do something like echo $arr['foo']. See below for tips on dumping/viewing the entire contents.

Objects are always converted to the string "Object". If you would like to print out the member variable values of an object for debugging reasons, read the paragraphs below. If you would like to find out the class name of which an object is an instance of, use get_class(). As of PHP 5, __toString() method is used if applicable.

Resources are always converted to strings with the structure "Resource id #1" where 1 is the unique number of the resource assigned by PHP during runtime. If you would like to get the type of the resource, use get_resource_type().

NULL is always converted to an empty string.

As you can see above, printing out the arrays, objects or resources does not provide you any useful information about the values themselves. Look at the functions print_r() and var_dump() for better ways to print out values for debugging.

You can also convert PHP values to strings to store them permanently. This method is called serialization, and can be done with the function serialize(). You can also serialize PHP values to XML structures, if you have WDDX support in your PHP setup.

String conversion to numbers

When a string is evaluated as a numeric value, the resulting value and type are determined as follows.

The string will evaluate as a float if it contains any of the characters '.', 'e', or 'E'. Otherwise, it will evaluate as an integer.

The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.

<?php
$foo = 1 + "10.5";                // $foo is float (11.5)
$foo = 1 + "-1.3e3";              // $foo is float (-1299)
$foo = 1 + "bob-1.3e3";          // $foo is integer (1)
$foo = 1 + "bob3";                // $foo is integer (1)
$foo = 1 + "10 Small Pigs";      // $foo is integer (11)
$foo = 4 + "10.2 Little Piggies"; // $foo is float (14.2)
$foo = "10.0 pigs " + 1;          // $foo is float (11)
$foo = "10.0 pigs " + 1.0;        // $foo is float (11)   
?>

For more information on this conversion, see the Unix manual page for strtod(3).

If you would like to test any of the examples in this section, you can cut and paste the examples and insert the following line to see for yourself what's going on:

<?php
echo "\$foo==$foo; type is " . gettype ($foo) . "<br />\n";
?>

Do not expect to get the code of one character by converting it to integer (as you would do in C for example). Use the functions ord() and chr() to convert between charcodes and characters.