ОсновноеRadiotalkПользовательское
Программирование
6   •   Посмотреть все темы

PHP - Полезные советы, приёмы, функции

 

20090
Dimitry @Dimitry
Как лучше всего сохранять IP адреса для статистики.

Внимание! При сохраниении в базу данных, скрипт работает неверно из-за символа \0.

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

Далее описанный мой способ сохранения и проверки IP адресов улучшает производительность в 4 раза. (посравнению с обычным)

1. Способ сохранения
- в Mysql используем поле text
- в файлах, каждому аккаунту свой фаил с IP например в папке ips: ips/user1.dat
- при использовании памяти, необходимо выделить достаточно

2. Метод кодирования и оптимизации
Каждый IP выглядит примерно так: 123.123.123.123
получаем 15 символов + один символ для разделения IP-адресов = 16
Столько требуется для записи одного IP, а для 1000 выходит уже 16кб информации!
Уменьшаем в 4 раза следующим способом:
  1. $ip=$_SERVER[REMOTE_ADDR]; $a=explode(".",$ip);
  2. $ip=chr($a[0]).chr($a[1]).chr($a[2]).chr($a[3]);

Теперь наш IP закодирован и составляет всего 4 символа.
Добавление в фаил с IP адресами происходит без разделителя, он нам не нужен.

3. Как проверить присутствие такого IP в переменной (из файла или Mysql) и добавить новый.
Мы получаем переменную с нашими закодированными IP адресами например из файла так:
$ips=file_get_contents("ips/user1.dat");

Теперь закодируем наш IP для проверки его в файле:
$ip=$_SERVER[REMOTE_ADDR]; $a=explode(".",$ip);
$ip=chr($a[0]).chr($a[1]).chr($a[2]).chr($a[3]);

Проверим наличие $ip в переменной $ips перебирая их через каждые 4 символа:
$found=0;
for($i=0;$i<strlen($ips);$i+=4) if($ips[$i]==$ip[0]) if($ip==substr($ips,$i,4)) {$found=1;break;}

if($ips[$i]==$ip[0]) - используется для оптимизации процесса поиска
Теперь если IP ненайден то добавим его:
предполагается что у вас php4 и функция file_put_contents не существует, тогда воспользуемся стандартной процедурой добавления записи в фаил...
$f=fopen("ips/user1.dat","a");
fwrite($f,$ip);
fclose($f);

Для работы с MYSQL и памятью читайте на php.net
Ну вот и всё, считаю что это самый оптимальный и продуктивный способ для создания подобных систем.

4. Вероятно нам нужно получить все IP адреса и перекодировать их сново в нормальный вид
// $ips - переменная с IP адресами
//создадим массив для раскодированных IP адресов
$aips=array();
for($i=0;$i<strlen($ips);$i+=4) $aips[]=ord($ips[$i]).'.'.ord($ips[$i+1]).'.'. ord($ips[$i+2]).'.'. ord($ips[$i+3]);
//выведем полученные адреса
for($i=0;$i<count($aips);$i++) echo $aips[$i]."<br>";

5. Весь код для работы на файлах
//decode IP
$ip=$_SERVER[REMOTE_ADDR]; $a=explode(".",$ip);
$ip=chr($a[0]).chr($a[1]).chr($a[2]).chr($a[3]);

//read IPs
$ips=file_get_contents("ips/user1.dat");

//search IP
$found=0;
for($i=0;$i<strlen($ips);$i+=4) if($ips[$i]==$ip[0]) if($ip==substr($ips,$i,4)) {$found=1;break;}

//save IP
if($found==0) {
$ips.=$ip;
$f=fopen("ips/user1.dat","a");
fwrite($f,$ip);
fclose($f);
}

//encode all IPs in Array
$aips=array();
for($i=0;$i<strlen($ips);$i+=4) $aips[]=ord($ips[$i]).'.'.ord($ips[$i+1]).'.'. ord($ips[$i+2]).'.'. ord($ips[$i+3]);

//output to see in HTML
for($i=0;$i<count($aips);$i++) echo $aips[$i]."<br>";

Вероятно это никому не пригодится, но может всё таки... или же я сам посмотрю когда забуду )


2007 copyright by demon

0
20090
Dimitry @Dimitry
Идеальная структура проекта - карказ сайта

Ну с чего начинается любой сайт, конечно же с index.php.
Что же в нём находится? Дизайн? Нет!
Из своего опыта я убедился в том что самым удобным образом, index.php выглядит так!

<?php
define("INDEX",1);

//include functions and connect to mysql
include("config.php");

//cache and design
register_shutdown_function("output");

function output() {
global $design,$title,$menu,$time_start,$to;
chdir(__DIR__);
$index=ob_get_contents();
ob_end_clean();
$design=file_get_contents("design.html");
$time_end=round((getmicrotime()-$time_start)*1000)/1000;
echo str_replace(array("%title%","%menu%","%index%","%time%"),array($title,$menu,$index,$time_end),$design);
}

//include menu
ob_start();
include("menu.php");
$menu=ob_get_contents();
ob_clean();

//include content
$to=$_GET[to];
if(preg_match("/^[a-z-]+$/",$to) && $to!="index" && is_file("$to.php")) include("$to.php");
else include("home.php");

?>

Думаю даже некоторые профи не знают этого метода и непонимают всех его плюсов.

Почему этот метод?

- вы можете использовать всего один шаблон с дизайном для всего сайта, нпример design.html
- вы можете использовать setcookie() и header() в любом месте, в любое время, в любых скриптах
- вы можете завершить выполнение модуля через die(), в любом месте в любое время и при этом дизайн будет полностью загружен
- вы определяете время выполнения скрипта в любых модулях, причём ничего не прописывая в них
- вы имеете высокую безопастность и гарантию того что скрипты запускаются именно через index.php, т.е. проходят всё что вы в нём укажите
- а главное - удобство, динамичность, мало PHP кода

Как работает этот метод?

1. Сначало индекс сообщает о том что он индекс: define("INDEX",1);
и для загрузки всех модулей сначало загружается сам индекс,
для этого каждый модуль должен начинаться с
<?
if(!defined("INDEX")) die('ошибка загрузки сайта');
$title="заголовок модуля";

2. Модули загружаются в основной дизайн и вызываются ссылками
index.php?to=module
В нашем методе мы регистрируем функцию output(), которая при завершении или ошибке скрипта загружает дизайн, который до этого просто кешируется в памяти.
register_shutdown_function("output");

Например так мы загружаем меню в переменную $menu

//include menu
ob_start();
include("menu.php");
$menu=ob_get_contents();
ob_clean();

А в функции output() читаем содержимое модуля в переменную $index, определяем время выполнения скрипта,читаем дизайн из файла и вносим в него меню, модуль, время , заголовок

$design=file_get_contents("design.html");
$time_end=round((getmicrotime()-$time_start)*1000)/1000;
echo str_replace(array("%title%","%menu%","%index%","%time%"),array($title,$menu,$index,$time_end),$design);

3. Как же выглядит шаблон дизайна design.html
<html>
<head>
<tilte>%title%</title>
</head>
<boby>
LOGO
<table width=800 height=100%>
<tr>
<td width=200>%menu%</td>
<td>%index%</td>
</tr>
</table>
<center>Загружено за %time% сек</center>
</body>
</html>

Что находится в config.php

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

<?
function getmicrotime(){
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
$time_start = getmicrotime();
?>

2008 copyright by demon

0
20090
Dimitry @Dimitry
Функция чтения строк файла с конца rfile()

Существует функция file(), которая читает весь фаил в массив строк.
Но возникает проблема при больших файлах, особенно если требуется прочитать только последние 10 строк.
Вот я и написал функцию rfile() которая читает несколько последних строк с конца файла и сохраняет их в массив, работает быстро!

function rfile($file,$num=10) {
$buf=1024;
if(!is_file($file)) return;
$f=fopen($file, 'r');
$pos=filesize($file)-1;
$c=0;$read="";
while($pos>0) {
$pos-=$buf;
if($pos<0) {$buf+=$pos;$pos=0;}
fseek($f,$pos);
$tmp=fread($f,$buf);
$c+=substr_count($tmp,"\n");
$read=$tmp.$read;
if($c>$num) break;
}
fclose($f);
$a=explode("\n",$read);$c=count($a); $r=array();
if(!$a[$c-1]) {unset($a[$c-1]);$c--;}
$start=$c-$num; if($start<0) $start=0;
for($i=$start;$i<$c;$i++) $r[]=$a[$i]."\n";
return $r;
}

2009 copyright by demon

0
20090
Dimitry @Dimitry
Функция удалённого получения содержимого страницы или файла через URL
(функция почти аналогична file_get_contents(), но работать будет даже с выключенной в PHP поддержкой удалённых подключений)

Примеры использования методом GET (через URL):

$content=req("http://domain.ru"); //скачаем содержимое главной страницы
$content=req("http://domain.ru/image.gif"); //скачаем картинку
$content=req("http://domain.ru/archiv.rar"); //скачаем архив
$content=req("http://domain.ru/info.php"); //скачаем HTML отдаваемый php файлом
$content=req("http://domain.ru/info.php?user=admin"); //скачаем HTML отдаваемый php файлом c GET параметрами user=admin
$content=req("http://domain.ru:81/index.php"); //скачиваем содержимое с указанием своего порта
$content=req("https://domain.ru/index.php"); //скачиваем содержимое через SSL (https) подключение
$content=req("https://domain.ru:443/index.php"); //скачиваем содержимое через SSL (https) подключение с указанием своего порта

Также можно делать запросы методом POST (как-бы через форму), достаточно указать второй параметр:

$content=req("http://domain.ru/login.php","user=admin&password=123");
(скачиваем содержимое передав параметры авторизации скрипту login.php методом POST)
$content=req("http://domain.ru/index.php?to=login","user=admin&password=123");
(совмесное использование методов GET (to=login) и POST (user=admin&password=123) )
$content=req("http://domain.ru/index.php?to=login",array("user"=>"admin","password"=>"123"));
(но лучше всего передавать параметры в POST запрос через массив)

Вы можете передать ещё один параметр $full=1, в этом случае будет выводиться не только содержимое, а заголовок запроса, заголовок ответа и содержимое. (разделяются 2мя переходами строки \r\n\r\n):
$content=req("http://domain.ru/index.php?to=login","user=admin&password=123",1);

Следующим параметром вы можете настроить таймаут для подключения/операции чтения: $timeout=3
а также принудительное завершение всего скрипта с выводом ошибки: $die=1


  1. //$content=req("https://user:pass@domain:443/file.php?get=variable","post=variable");
  2. function req($url,$post="",$full=0,$timeout=3,$die=1) {
  3. $err=array("Неправильный URL запроса!","Невозможно подключиться!","Невозможно получить данные!");
  4. $url=@parse_url($url);
  5. if(!ereg("^(http|https)://[a-z0-9.:-]+/","$url[scheme]://$url[host]/")) {if($die) die("$err[0]"); return;}
  6. if(!$url[path]) $url[path]="/"; if($url[scheme]=="https") $type="ssl://"; else $type="";
  7. if(!$url[port]) {if($url[scheme]=="https") $url[port]=443; else $url[port]=80;}
  8. $method="GET"; if($url[query]) $url[query]="?$url[query]";
  9. $contents=""; if($url[user] && $url[pass]) $contents.="Authorization: Basic ".base64_encode("$url[user]:$url[pass]")."\r\n";
  10. if($post) {$method="POST"; if(is_array($post)) $post=http_build_query($post); else $post=str_replace(array("%3D","%26"),array("=","&"),urlencode($post)); $contents.="Content-Type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($post)."\r\n";}
  11. $send="$method $url[path]$url[query] HTTP/1.0\r\nHost: $url[host]\r\nUser-Agent: Mozilla\r\n$contents\r\n$post";
  12. //$socket_context = stream_context_create(array('socket' => array('bindto' => '123.123.123.123:0')));
  13. if(!$f = @stream_socket_client("$type$url[host]:$url[port]", $errno,$errstr, $timeout, STREAM_CLIENT_CONNECT)) {if($die) die("$err[1]"); return;}
  14. else {
  15. fwrite($f,$send); stream_set_timeout($f,$timeout); $info=stream_get_meta_data($f);
  16. if($type) stream_set_blocking($f, FALSE); $time_start=time();
  17. $r=""; while(!feof($f) && !$info['timed_out']) {$read=fread($f,8192); $r.=$read; $info=stream_get_meta_data($f); if($type) {if(!$read) {if($usleep<1000000) $usleep+=10000; usleep($usleep);} if(time()-$time_start>$timeout) $info['timed_out']=1;}}
  18. fclose($f);
  19. if($info['timed_out']) { if($die) die("$err[2]"); return;}
  20. }
  21. if($full) return "$send$r";
  22. $pos=strpos($r,"\r\n\r\n");if($pos) $r=substr($r,$pos+4);
  23. return $r;
  24. }

2009 copyright by demon

0
20090
Dimitry @Dimitry
Скриптовая эмуляция изменения глобальных настроек в php.ini
- magic_quotes_gpc
- magic_quotes_runtime
- register_globals
- register_long_arrays

Если вам попался хостинг на котором нет возможности прописать эти глобальные настройки в .htaccess или php.ini, то вы можете воспользоваться этим уникальным скриптом.
Просто вставьте этот скрипт в конфиг фаил вашего сайта или в самый верх каждой страницы/пхп-файла.
Этот скрипт изменит данные настройки php и не оставит никаких следов вмешательств после себя.
Данный скрипт поможет вам запускать скрипты созданные на php3, php4 и php5.
Пропистаь скрипт в пхп-файле можно такой строчкой: include_once("ini-set.php");

А вот и сам скрипт: ini-set.php
  1. <?php
  2. //PHP HARD INI-SETTINGS EMULATION by Dimitry Bansikov
  3. $magic_quotes_gpc=0; //used in php < 5.2.3
  4. set_magic_quotes_runtime(0); //don't need to change normally
  5. $register_globals=1; //used in php < 5.2.3
  6. $register_long_arrays=0; //used in php < 4.0.2
  7. //magic_quotes_gpc ON
  8. if ($magic_quotes_gpc && !get_magic_quotes_gpc()) {
  9. foreach ($_GET as $k => $v) $_GET[$k]=addslashes($v);
  10. foreach ($_POST as $k => $v) $_POST[$k]=addslashes($v);
  11. foreach ($_COOKIE as $k => $v) $_COOKIE[$k]=addslashes($v);
  12. ini_set('magic_quotes_gpc', true);
  13. define("RENEW_MQ",1);
  14. }
  15. //magic_quotes_gpc OFF
  16. if (!$magic_quotes_gpc && get_magic_quotes_gpc()) {
  17. foreach ($_GET as $k => $v) $_GET[$k]=stripslashes($v);
  18. foreach ($_POST as $k => $v) $_POST[$k]=stripslashes($v);
  19. foreach ($_COOKIE as $k => $v) $_COOKIE[$k]=stripslashes($v);
  20. ini_set('magic_quotes_gpc', false);
  21. define("RENEW_MQ",1);
  22. }
  23. //update $_REQUEST for magic_quotes_gpc
  24. if(defined("RENEW_MQ")) {
  25. foreach ($_GET as $k => $v) $_REQUEST[$k]=$v;
  26. foreach ($_POST as $k => $v) $_REQUEST[$k]=$v;
  27. foreach ($_COOKIE as $k => $v) $_REQUEST[$k]=$v;
  28. }
  29. //register_globals ON
  30. if ($register_globals && (!ini_get('register_globals') || defined("RENEW_MQ"))) {
  31. if(defined("RENEW_MQ")) foreach ($_REQUEST as $k => $v) unset($GLOBALS[strtolower($k)]);
  32. $superglobals = array($_SERVER, $_ENV, $_FILES, $_COOKIE, $_POST, $_GET);
  33. if (isset($_SESSION)) array_unshift($superglobals, $_SESSION);
  34. foreach ($superglobals as $superglobal) extract($superglobal, EXTR_SKIP);
  35. ini_set('register_globals', true);
  36. }
  37. //register_globals OFF
  38. if (!$register_globals && ini_get('register_globals')) {
  39. $superglobals = array($_SERVER, $_ENV,$_FILES, $_COOKIE, $_POST, $_GET);
  40. if (isset($_SESSION)) array_unshift($superglobals, $_SESSION);
  41. foreach ($superglobals as $superglobal) {
  42. foreach ($superglobal as $k => $v) unset($GLOBALS[$k]);
  43. }
  44. ini_set('register_globals', false);
  45. }
  46. //register_long_arrays ON
  47. if ($register_long_arrays && (!ini_get('register_long_arrays') || defined("RENEW_MQ"))) {
  48. $HTTP_SERVER_VARS=$_SERVER; $HTTP_ENV_VARS=$_ENV;
  49. $HTTP_POST_FILES=$_FILES; $HTTP_COOKIE_VARS=$_COOKIE;
  50. $HTTP_POST_VARS=$_POST; $HTTP_GET_VARS=$_GET;
  51. if(isset($_SESSION)) $HTTP_SESSION_VARS=$_SESSION;
  52. ini_set('register_long_arrays', true);
  53. }
  54. //register_long_arrays OFF
  55. if (!$register_long_arrays && ini_get('register_long_arrays')) {
  56. unset($HTTP_SERVER_VARS, $HTTP_ENV_VARS, $HTTP_POST_FILES, $HTTP_COOKIE_VARS, $HTTP_POST_VARS, $HTTP_GET_VARS,$HTTP_SESSION_VARS);
  57. ini_set('register_long_arrays', false);
  58. }
  59. //unset used variables
  60. unset($magic_quotes_gpc,$register_globals,$register_long_arrays,$superglobals,$superglobal,$k,$v);
  61. //to check this script uncomment
  62. //echo "<pre>";print_r($GLOBALS);
  63. ?>

2009 copyright by demon

0
20090
Dimitry @Dimitry
Функция для работы с базой данных для удобства и исключения инъекций

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

Функция в процессе тестирования!

Описание:
$r=sql($table,$type,$fields,$where="",$limit="");
- данные передаваемые в $where требую предварительную фильтрацию на уязвимости
- $fields может быть как массивом, так и строкой, если строка - то требуется проверка на уязвимости
В получаемом результате:
$r[num] - кол-во полученных строк для select или ID добавленной строки для insert или кол-во затронутых строк для update/delete
$r[res] - результат запроса select для последующего чтения строк
$r[time] - время выполнения запроса

Пример подключения к базе:

$link = mysql_connect('localhost', 'MyLoginInMysql', 'MyPassword') or die('Ошибка соединения: ' . mysql_error());

Примеры запросов через фунцию:

$r=sql("db.table","select",$fields,"id=1","0,100");
$r=sql("db.table","insert",$fields);
$r=sql("db.table","update",$fields,"id=1");
$r=sql("db.table","delete","","id=1");

Подробные примеры:
$r=sql("db.table","select","*","id=1","0,100"); - получаем все поля (*) не более 100 записей у которых id=1
$r=sql("db.table","select",array("id","text"),"id=1","0,100"); - тоже самое, но с базы берём только поля id и text
while($row=mysql_fetch_assoc($r[res])) echo "$row[id] - $row[text]<br>"; - так мы перебираем полученные строки
$r=sql("db.table","insert",array("name"=>"MyName","text"=>"MyText")); - вставляем в базу запись с $name="MyName" и $text = "MyText"
$r[num] - это ID идентификатор добавленной строки в случае если в базе задан primary key

function sql($table,$type,$fields,$where="",$limit="") {
$time_start=microtime(1);
if(!preg_match("/^[a-z0-9.`_-]+$/i",$table)) return false;
$type=strtolower($type); if(!in_array($type,array("select","insert","update","delete"))) die("Query type $type failed.");
if(strlen($where)) $where="where $where";
if(strlen($limit)) {if(preg_match("/^[0-9,]+$/i",$limit)) $limit="limit $limit"; else die("Query limit $limit failed.");}
if(is_string($fields) && strlen($fields) && !preg_match("/^[a-z0-9, \*\`_-]+$/i",$fields)) die("Query fields $fields failed.");
$setfields=""; $values="";
if(is_array($fields)) {
if($type=="select") $fields=implode(",",$fields);
if($type=="insert") foreach($fields as $k=>$v) {if($setfields) $setfields.=","; $setfields.="`$k`"; if($values) $values.=","; $values.="'".mysql_real_escape_string($v)."'"; }
if($type=="update") foreach($fields as $k=>$v) {if($setfields) $setfields.=","; $setfields.="`$k`='".mysql_real_escape_string($v)."'"; }
}
if($type=="select") $sql="select $fields from $table $where $limit";
if($type=="insert") $sql="insert into $table ($setfields) values ($values)";
if($type=="update") $sql="update $table set $setfields $where $limit";
if($type=="delete") $sql="delete from $table $where $limit";
$res=mysql_query($sql) or die(mysql_error());
if($type=="select") $r=mysql_num_rows($r);
if($type=="insert") $r=mysql_insert_id();
if($type=="update" || $type=="delete") $r=mysql_affected_rows();
$time=round(microtime(1)-$time_start,3);
return array("num"=>$r,"res"=>$res,"time"=>$time);
}

0
8497
Степан @Trilby
Я конечно даже близко с Димой не стою, когда речь идёт о программировании, но все же попробую разбавить тему своим постом 😀
Идея, которую я нагло утащил у habrastorage.org, но в гуглах реализации не нашел, а именно - загрузка файла на сервер с проверкой контрольной суммы и присваиванием имени вида md5check.ext
Для чего это нужно? Просто для предотвращения закачки идентичных файлов и создания уникальных имён файлам в одной папке.

Собственно, реализация. В своем примере я просто переберу готовую форму, взятую **********.

Абсолютно нетронутый upload.html
  1. <html>
  2. <head>
  3. <title>Загрузка файлов на сервер</title>
  4. <meta http-equiv="content-type" content="text/html; charset=utf-8">
  5. </head>
  6. <body>
  7. <h2><p><b> Форма для загрузки файлов </b></p></h2>
  8. <form action="upload.php" method="post" enctype="multipart/form-data">
  9. <input type="file" name="filename"><br>
  10. <input type="submit" value="Загрузить"><br>
  11. </form>
  12. </body>
  13. </html>

upload.php
  1. <html>
  2. <head>
  3. <title>Результат загрузки файла</title>
  4. <meta http-equiv="content-type" content="text/html; charset=utf-8">
  5. </head>
  6. <body>
  7. <?php
  8. if($_FILES["filename"]["size"] > 1024*1024*5)
  9. {
  10. echo ("Размер файла превышает 5 МБ");
  11. exit;
  12. }
  13. // Проверяем загружен ли файл
  14. if(is_uploaded_file($_FILES["filename"]["tmp_name"]))
  15. {
  16. // Если файл загружен успешно, перемещаем его
  17. // из временной директории в конечную
  18. $folder = '/home/user/uploadfolder/'; //папка для загрузки файла
  19. $link = 'http://example.com/uploads/'; //её расположение в адресе сайта. Просто для вывода пользователю.
  20. $ext = strtolower(substr(strrchr($_FILES['filename']['name'],'.'), 1)); //расширение файла. Кто знает лучший способ узнать расширение?
  21. $md5filename = md5_file($_FILES["filename"]["tmp_name"]); // проверка md5-суммы загружаемого файла
  22. move_uploaded_file($_FILES["filename"]["tmp_name"], $folder.$md5filename.".".$ext); //перемещение с переименованием файла в md5-сумму.
  23. echo $link.$md5filename.".".$ext; //вывод пользователю ссылки на загруженный файл.
  24. } else {
  25. echo("Ошибка загрузки файла");
  26. }
  27. ?>
  28. </body>
  29. </html>

А теперь попробуйте загрузить в форму один и тот же файл пару раз)

Отредактировано Trilby - 16.07.2011
0
20090
Dimitry @Dimitry
Готовимся к IPv6 - валидация IP адресов

Для общей информации, IPv4 длина от 7-15, IPv6 длина от 3-39.
Функция проверки IP адреса на валидность и протокол.

function checkip($ip) {
$x=explode(".",$ip); $c=count($x);
if($c==4) {$ver=4; for($i=0;$i<$c;$i++) if(!is_numeric($x[$i]) || $x[$i]<0 || $x[$i]>255) $ver=0;}
elseif(preg_match("/^[0-9a-f:]{3,39}$/",$ip)) {$ver=6; $x=explode(":",$ip); $c=count($x); if($c<3 || $c>8) $ver=0; for($i=0;$i<$c;$i++) if(strlen($x[$i])>4) $ver=0; }
return (int)$ver;
}

Пример использования:
if($ver=checkip($_SERVER['REMOTE_ADDR'])) echo "IP-Адрес верный и соответствует протоколу IPv$ver";
else echo "IP-Адрес не верный!";

Так как существуют сокращённые формы записи IPv6 адреса, возможно нам потребуется узнать его полную форму.
Раскрытие сокращённого IPv6 адреса: (например раскрываем ::1 - локальный адрес = 0000:0000:0000:0000:0000:0000:0000:0001 )

function ipv6full($ip) {
$x=explode(":",$ip); $c=count($x);
if($c<8) {$d=str_repeat(":",8-$c); $ip=str_replace("::","::$d",$ip); $x=explode(":",$ip); $c=count($x); }
for($i=0;$i<$c;$i++) if(($l=strlen($x[$i]))!=4) {$d=str_repeat("0",4-$l); $x[$i]=$d.$x[$i];}
return implode(":",$x);
}

0
20090
Dimitry @Dimitry
Инструкция по переходу с windows-1251 на UTF-8

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

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

2. Анализ и автоперекодировка. Перекодируем все файлы с русскоязычными названиями в новую кодировку, а также содержимое для указанных типов файлов.
Для этого я написал не очень сложный скриптик. По умолчанию он выводит файлы которые хочет перекодировать, чтобы выполнить перекодировку нужно нажать на ссылку rename, recode или mbstr set. Последнее в данном случае подставляет "mb_" перед строковыми функциями, делать это нужно только профессионалам!
Закачайте php файл reencoding.php и запустите его:
  1. <?
  2. function scan($dir) {
  3. global $script;
  4. $d = dir ($dir); if(!$d) return;
  5. while(false!==($f = $d->read())) if($f != "." && $f != ".." && $f!=$script) recode($dir."/".$f);
  6. $d->close();
  7. }
  8. function recode($dir) {
  9. global $from,$to,$types;
  10. $enc=iconv($from,"$to//IGNORE",$dir);
  11. if($enc!=$dir) {
  12. echo "<b>FILENAME:</b> $dir => $enc<br>";
  13. if($_GET[rename]) {rename($dir,$enc); $dir=$enc;}
  14. }
  15. if(is_dir($dir)) {scan($dir); return;}
  16. $type=explode(".",$dir); $type=$type[count($type)-1];
  17. if(!in_array($type,$types)) return;
  18. $content=file_get_contents($dir);
  19. $econtent=iconv($from,"$to//IGNORE",$content);
  20. if($econtent!=$content) {
  21. echo "<b>CONTENT:</b> $dir<br>";
  22. if($_GET[recode]) {file_put_contents($dir,$econtent);$content=$econtent;}
  23. }
  24. $mblog=""; $a=explode("\n",$content);
  25. for($i=0;$i<count($a);$i++) {
  26. $a1 = preg_replace('/(\W{1})(strlen|strpos|strrpos|substr|strtolower|strtoupper|stripos|strripos|strstr|stristr|strrchr|substr_count)\(/' ,'$1mb_$2(',$a[$i]);
  27. if($a[$i]!=$a1) $mblog.="<span style='color:gray; font-size:11px;'>".($i+1).": ".htmlspecialchars($a[$i])."</span><br>";
  28. if(preg_match("/(utf-8|utf8|cp1251|windows-1251)/i",$a[$i])) echo "<b>Encoding used</b>: $dir - <span style='color:blue; font-size:11px;'>".($i+1).": ".htmlspecialchars($a[$i])."</span><br>";
  29. if(preg_match("/(preg_)/i",$a[$i])) echo "<b>Preg used</b>: $dir - <span style='color:green; font-size:11px;'>".($i+1).": ".htmlspecialchars($a[$i])."</span><br>";
  30. }
  31. if($mblog) echo "<b>MBSTR CHECK</b>: $dir<br>$mblog";
  32. if($_GET[mbstrset] && $mblog) {
  33. $content = preg_replace('/([[:^alpha:]]{1})(strlen|strpos|strrpos|substr|strtolower|strtoupper|stripos|strripos|strstr|stristr|strrchr|substr_count)\(/' ,'$1mb_$2(',$content);
  34. file_put_contents($dir,$content);
  35. }
  36. }
  37. $script="reencoding.php";
  38. $from="windows-1251";
  39. $to="utf-8";
  40. $types=array("php","html"); //file types to encode content
  41. $dir="./";
  42. scan($dir);
  43. echo "<br><a href=?>CHECK ALL</a> | <a href=?rename=1>RENAME FILES</a> | <a href=?recode=1>RECODE CONTENT</a> | <a href=?mbstrset=1>MBSTR SET FUNCTIONS</a><br><br>";
  44. if($_GET[rename]) echo "<b>RENAME COMPLETE</b><br>";
  45. if($_GET[recode]) echo "<b>RECODE COMPLETE</b><br>";
  46. if($_GET[mbstrset]) echo "<b>MBSTR SET COMPLETE</b><br>";
  47. ?>

3 MB_STR функции и не только. Наши старые PHP функции для работы со строками не годятся, им нужны приставки mb_ , можно воспользоваться скриптом поиска таких функций и заменить их на новые с mb_ автоматически. (только для опытных) Также чтобы не задавать кодировку в каждой такой функции, нужно указать её в конфиге сайта по умолчанию так:
mb_internal_encoding("utf-8");
Но и это ещё не всё, скрипт также покажет вам и все найденные "preg_" функции, дело в том что если в таких функциях используются не латинские символы, то необходимо добавить модификатор u. Например так:
preg_match("/^[a-zа-я]+$/u"$str)
Кроме этого, возможны и другие функции которые необходимо доработать под UTF-8, но на данный момент это самые важные.

4. Базы данных. Желательно перекодировать базу в UTF-8, но для начала мы можем просто включить для PHP связь с базой (только MySQL5) в кодировке UTF-8, для этого в коде после подключения к базе (mysql_connect) достаточно прописать или изменить такую строку:
mysql_query("SET NAMES 'utf8'");

Если вы всё таки нужно перекодировать базу в UTF-8, то для каждой таблицы нужно выполнять этот запрос:
ALTER TABLE `db_name`.`table_name` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci

5. META и Header заголовки. Ну и на последок меняем кодировку для HTML шаблонов, в PHP заголовках и в mail() заголовках.
Выше приведённый скрипт сам найдёт все места где указана старая кодировка (но только в файлах поиска), например:
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
меняем на
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
или
Content-Type: text/plain; charset="windows-1251"
меняем на
Content-Type: text/plain; charset="utf-8"

0
20090
Dimitry @Dimitry
Как защитить сайт и сервер от перегрузки из-за DDOS атак
(поисковые боты также могут быть заблокированы, поэтому надо добавлять их IP в исключения, самые важные я добавил)

Достаточно добавить данный код после <? в самый верх атакуемой страницы, обычно это index.php
Красный параметр означает что за 1 секунду открыть страницу сайта с одного IP можно только 1 раз,
если поставить 2, то сайт можно будет открывать только 1 раз в 2 секунды, это улучшает защиту!

function checkddos($sec) {
$ban=0; $file="protect.dat"; $time=time(); $ip=$_SERVER[REMOTE_ADDR];
$whitelist=array("127.0.0.1","91.42.*.*","217.235.*.*","213.180.*.*","87.250.*.*","77.88.*.*","66.249.*.*","188.72.*.*");
$x=explode(".",$ip); foreach($whitelist as $ip1) if(preg_match("/^$x[0]\.($x[1]|\*)\.($x[2]|\*)\.($x[3]|\*)$/",$ip1)) return 0;
$f=@fopen($file,"r"); if($f) {clearstatcache(); flock($f,LOCK_SH); $r=@fread($f,filesize($file)); fclose($f);}
$a=unserialize($r);
if($a[$ip]+$sec>=$time) $ban=1; $a[$ip]=$time;
foreach($a as $k=>$v) if($v+$sec+1<$time) unset($a[$k]);
file_put_contents($file,serialize($a),LOCK_EX);
return $ban;
}
if(checkddos(1)) die("503 Service not available");

0
20090
Dimitry @Dimitry
Получение информации о состоянии Shoutcast 2 сервера, название песни и так далее

Так как данный сервер не поддерживается разработчиками, я отказался от его использования в пользу Icecast 2.
Тем не менее данный код может быть полезен тем кто всё ещё использует этот сервер.

  1. <?
  2. //SHOUTCAST 2 parsing
  3. function shoutcast2($server,$port,$sid=1) {
  4. function filter($str) {return htmlspecialchars($str);}
  5. $info=array();
  6. $info[port]=$port;
  7. $info[server]="ShoutCast2";
  8. //get html
  9. $html=req("http://$server:$port/index.html?sid=$sid","",0,3,0);
  10. //parse
  11. $a=explode('valign="top">',$html);
  12. $a[0]="";$end=explode("</b>",$a[count($a)-1]); $a[count($a)-1]=$end[0];
  13. //filter
  14. for($i=1;$i<count($a);$i++) {
  15. $a[$i]=str_replace('<font class="default">','',$a[$i]);
  16. $x=explode(': ',$a[$i]); $name=$x[0];
  17. $var=strip_tags($x[1]); //clear tags
  18. //set my vars
  19. if($name=="Server Status") {if(substr_count($var,"Server is currently up")) $info[online]=1;}
  20. //Stream is up at 128 kbps with 0 of 10 listeners (0 unique)
  21. if($name=="Stream Status") {$x=explode(" ",$var); $info[kbps]=(int)$x[4]; $info[listeners]=(int)$x[7]; $info[limit]=(int)$x[9]; $info[ulisteners]=(int)substr($x[11],1); }
  22. if($name=="Listener Peak") $info[plisteners]=filter($var);
  23. if($name=="Stream Name") $info[djname]=mb_substr(filter($var),0,50);
  24. if($name=="Current Song") $info[song]=filter($var);
  25. if($name=="Stream Genre") $info[genre]=filter($var);
  26. if($name=="Stream ICQ") $info[icq]=filter($var);
  27. if($name=="Stream AIM") $info[aim]=filter($var);
  28. if($name=="Stream URL") $info[url]=filter($var);
  29. }
  30. return $info;
  31. }
  32. //Пример использования функции:
  33. $info=shoutcast2("radioserver.ru",8000,1);
  34. print_r($info);
  35. ?>

В качестве бонуса, пример конфига сервера sc_serv.conf:
MaxUser=100
Password=djhiddenpass
PortBase=8000
ScreenLog=0
W3CEnable=No
W3CLog=sc_w3c.dat
LogFile=sc_servlog.dat
SrcIP=ANY
DestIP=ANY
Yport=80
NameLookups=0
AdminPassword=adminhiddenpass
AutoDumpUsers=0
AutoDumpSourceTime=30
IntroFile=intro%d.mp3
TitleFormat=%s
URLFormat=%s
PublicServer=No
AllowRelay=Yes
AllowPublicRelay=No
#relay server commented
#yp2=1
#RelayServer=relayradio.ru[host]
#RelayPort=8000[port]
#streamid=1

Ну и самая новая версия Shutcast 2 севрера:
**********

0
20090
Dimitry @Dimitry
Приводим старый PHP код с <? в порядок

Для правильной подсветки и с учётом на будущее, лучше не использовать открытие php с помощью <? или <?=, а использоваться только <?php. Данная команда поможет рекурсивно найти все места в вашем проекте где есть сокращённый вид открытия php кода. Нужно зайти в папку проекта через ssh и выполнить:

cd /home/user/website.ru
egrep -s -R -e '<\?[e=[:space:]]{1}' -e '<\?$' ./

Совет на будущее

Хотите без проблем и эскейпов вставлять HTML код с PHP переменными прямо в php скрипт, воспользуйтесь способом hededoc.
Пример с вставкой простых переменных и элементов массива:
  1. <?php
  2. $number=15;
  3. $a=array('name'=>'Vasya','years'=>25);
  4. //тут вставляем HTML код
  5. echo <<<HTML
  6. Тут можно писать любые кавычки, например "эти" или 'эти'.
  7. Эй, привет {$a['name']}, как дела?
  8. Твой номер $number, да?
  9. HTML;
  10. ?>

0





Согласие на обработку данных на нашем сайте

Продолжая просматривать страницу, вы соглашаетесь с использованием файлов «Cookie» и с Политикой конфиденциальности «Privacy».
Наверху или внизу страницы вы можете изменить валюту и язык по умолчанию. Английская версия сайта ещё находится в доработке и доступна не полностью.