Форумы » Программирование »

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



19339
Dimitry
[b]Как лучше всего сохранять IP адреса для статистики.[/b]

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

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

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

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

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

[b]3. Как проверить присутствие такого IP в переменной (из файла или Mysql) и добавить новый.[/b]
Мы получаем переменную с нашими закодированными IP адресами например из файла так:
[quote]$ips=file_get_contents("ips/user1.dat");[/quote]
Теперь закодируем наш IP для проверки его в файле:
[quote]$ip=$_SERVER[REMOTE_ADDR]; $a=explode(".",$ip);
$ip=chr($a[0]).chr($a[1]).chr($a[2]).chr($a[3]);[/quote]
Проверим наличие $ip в переменной $ips перебирая их через каждые 4 символа:
[quote]$found=0;
for($i=0;$i<strlen($ips);$i+=4) if($ips[$i]==$ip[0]) if($ip==substr($ips,$i,4)) {$found=1;break;}[/quote]
[color=red]if($ips[$i]==$ip[0])[/color] - используется для оптимизации процесса поиска
Теперь если IP ненайден то добавим его:
предполагается что у вас php4 и функция file_put_contents не существует, тогда воспользуемся стандартной процедурой добавления записи в фаил...
[quote]$f=fopen("ips/user1.dat","a");
fwrite($f,[color=blue]$ip[/color]);
fclose($f);[/quote]
Для работы с MYSQL и памятью читайте на php.net
Ну вот и всё, считаю что это самый оптимальный и продуктивный способ для создания подобных систем.

[b]4. Вероятно нам нужно получить все IP адреса и перекодировать их сново в нормальный вид[/b]
[quote]// $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>";[/quote]
[b]5. Весь код для работы на файлах[/b]
[quote]//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>";[/quote]
Вероятно это никому не пригодится, но может всё таки... или же я сам посмотрю когда забуду )


[b]2007 copyright by demon[/b]
19339
Dimitry
[b]Идеальная структура проекта - карказ сайта[/b]

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

[quote]<?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");

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

[b]Почему этот метод?[/b]

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

[b]Как работает этот метод?[/b]

1. Сначало индекс сообщает о том что он индекс: define("INDEX",1);
и для загрузки всех модулей сначало загружается сам индекс,
для этого каждый модуль должен начинаться с
[quote]<?
if(!defined("INDEX")) die('ошибка загрузки сайта');
$title="заголовок модуля";[/quote]
2. Модули загружаются в основной дизайн и вызываются ссылками
index.php?to=module
В нашем методе мы регистрируем функцию output(), которая при завершении или ошибке скрипта загружает дизайн, который до этого просто кешируется в памяти.
register_shutdown_function("output");

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

[quote]//include menu
ob_start();
include("menu.php");
$menu=ob_get_contents();
ob_clean();[/quote]
А в функции output() читаем содержимое модуля в переменную $index, определяем время выполнения скрипта,читаем дизайн из файла и вносим в него меню, модуль, время , заголовок

[quote]$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);[/quote]
3. Как же выглядит шаблон дизайна design.html
[quote]<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>[/quote]
[b]Что находится в config.php[/b]

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

[quote]<?
function getmicrotime(){
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
$time_start = getmicrotime();
?>[/quote]
[b]2008 copyright by demon[/b]
19339
Dimitry
[b]Функция чтения строк файла с конца rfile()[/b]

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

[quote]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;
}[/quote]
[b]2009 copyright by demon[/b]
19339
Dimitry
[b]Функция удалённого получения содержимого страницы или файла через URL[/b]
(функция почти аналогична file_get_contents(), но работать будет даже с выключенной в PHP поддержкой удалённых подключений)

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

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

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

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

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

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


[code]//$content=req("https://user:pass@domain:443/file.php?get=variable","post=variable");
function req($url,$post="",$full=0,$timeout=3,$die=1) {
$err=array("Неправильный URL запроса!","Невозможно подключиться!","Невозможно получить данные!");
$url=@parse_url($url);
if(!ereg("^(http|https)://[a-z0-9.:-]+/","$url[scheme]://$url[host]/")) {if($die) die("$err[0]"); return;}
if(!$url[path]) $url[path]="/"; if($url[scheme]=="https") $type="ssl://"; else $type="";
if(!$url[port]) {if($url[scheme]=="https") $url[port]=443; else $url[port]=80;}
$method="GET"; if($url[query]) $url[query]="?$url[query]";
$contents=""; if($url[user] && $url[pass]) $contents.="Authorization: Basic ".base64_encode("$url[user]:$url[pass]")."\r\n";
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";}
$send="$method $url[path]$url[query] HTTP/1.0\r\nHost: $url[host]\r\nUser-Agent: Mozilla\r\n$contents\r\n$post";
//$socket_context = stream_context_create(array('socket' => array('bindto' => '123.123.123.123:0')));
if(!$f = @stream_socket_client("$type$url[host]:$url[port]", $errno,$errstr, $timeout, STREAM_CLIENT_CONNECT)) {if($die) die("$err[1]"); return;}
else {
fwrite($f,$send); stream_set_timeout($f,$timeout); $info=stream_get_meta_data($f);
if($type) stream_set_blocking($f, FALSE); $time_start=time();
$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;}}
fclose($f);
if($info['timed_out']) { if($die) die("$err[2]"); return;}
}
if($full) return "$send$r";
$pos=strpos($r,"\r\n\r\n");if($pos) $r=substr($r,$pos+4);
return $r;
}[/code]
[b]2009 copyright by demon[/b]
19339
Dimitry
[b]Скриптовая эмуляция изменения глобальных настроек в php.ini[/b]
- magic_quotes_gpc
- magic_quotes_runtime
- register_globals
- register_long_arrays

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

А вот и сам скрипт: [b]ini-set.php[/b]
[code]<?php

//PHP HARD INI-SETTINGS EMULATION by Dimitry Bansikov
$magic_quotes_gpc=0; //used in php < 5.2.3
set_magic_quotes_runtime(0); //don't need to change normally
$register_globals=1; //used in php < 5.2.3
$register_long_arrays=0; //used in php < 4.0.2

//magic_quotes_gpc ON
if ($magic_quotes_gpc && !get_magic_quotes_gpc()) {
foreach ($_GET as $k => $v) $_GET[$k]=addslashes($v);
foreach ($_POST as $k => $v) $_POST[$k]=addslashes($v);
foreach ($_COOKIE as $k => $v) $_COOKIE[$k]=addslashes($v);
ini_set('magic_quotes_gpc', true);
define("RENEW_MQ",1);
}
//magic_quotes_gpc OFF
if (!$magic_quotes_gpc && get_magic_quotes_gpc()) {
foreach ($_GET as $k => $v) $_GET[$k]=stripslashes($v);
foreach ($_POST as $k => $v) $_POST[$k]=stripslashes($v);
foreach ($_COOKIE as $k => $v) $_COOKIE[$k]=stripslashes($v);
ini_set('magic_quotes_gpc', false);
define("RENEW_MQ",1);
}
//update $_REQUEST for magic_quotes_gpc
if(defined("RENEW_MQ")) {
foreach ($_GET as $k => $v) $_REQUEST[$k]=$v;
foreach ($_POST as $k => $v) $_REQUEST[$k]=$v;
foreach ($_COOKIE as $k => $v) $_REQUEST[$k]=$v;
}

//register_globals ON
if ($register_globals && (!ini_get('register_globals') || defined("RENEW_MQ"))) {
if(defined("RENEW_MQ")) foreach ($_REQUEST as $k => $v) unset($GLOBALS[strtolower($k)]);
$superglobals = array($_SERVER, $_ENV, $_FILES, $_COOKIE, $_POST, $_GET);
if (isset($_SESSION)) array_unshift($superglobals, $_SESSION);
foreach ($superglobals as $superglobal) extract($superglobal, EXTR_SKIP);
ini_set('register_globals', true);
}
//register_globals OFF
if (!$register_globals && ini_get('register_globals')) {
$superglobals = array($_SERVER, $_ENV,$_FILES, $_COOKIE, $_POST, $_GET);
if (isset($_SESSION)) array_unshift($superglobals, $_SESSION);
foreach ($superglobals as $superglobal) {
foreach ($superglobal as $k => $v) unset($GLOBALS[$k]);
}
ini_set('register_globals', false);
}

//register_long_arrays ON
if ($register_long_arrays && (!ini_get('register_long_arrays') || defined("RENEW_MQ"))) {
$HTTP_SERVER_VARS=$_SERVER; $HTTP_ENV_VARS=$_ENV;
$HTTP_POST_FILES=$_FILES; $HTTP_COOKIE_VARS=$_COOKIE;
$HTTP_POST_VARS=$_POST; $HTTP_GET_VARS=$_GET;
if(isset($_SESSION)) $HTTP_SESSION_VARS=$_SESSION;
ini_set('register_long_arrays', true);
}
//register_long_arrays OFF
if (!$register_long_arrays && ini_get('register_long_arrays')) {
unset($HTTP_SERVER_VARS, $HTTP_ENV_VARS, $HTTP_POST_FILES, $HTTP_COOKIE_VARS, $HTTP_POST_VARS, $HTTP_GET_VARS,$HTTP_SESSION_VARS);
ini_set('register_long_arrays', false);
}

//unset used variables
unset($magic_quotes_gpc,$register_globals,$register_long_arrays,$superglobals,$superglobal,$k,$v);

//to check this script uncomment
//echo "<pre>";print_r($GLOBALS);
?>[/code]
[b]2009 copyright by demon[/b]
19339
Dimitry
[b]Функция для работы с базой данных для удобства и исключения инъекций[/b]

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

[color=red]Функция в процессе тестирования![/color]

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

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

$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

[quote]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);
}[/quote]
8497
Trilby
Я конечно даже близко с Димой не стою, когда речь идёт о программировании, но все же попробую разбавить тему своим постом 😀
Идея, которую я нагло утащил у habrastorage.org, но в гуглах реализации не нашел, а именно - загрузка файла на сервер с проверкой контрольной суммы и присваиванием имени вида [b]md5check[/b].[b]ext[/b]
Для чего это нужно? Просто для предотвращения закачки идентичных файлов и создания уникальных имён файлам в одной папке.

Собственно, реализация. В своем примере я просто переберу готовую форму, взятую [url=http://softtime.ru/scripts/upload.php]отсюда[/url].

[b]Абсолютно нетронутый upload.html[/b]
[code]<html>
<head>
<title>Загрузка файлов на сервер</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
<h2><p><b> Форма для загрузки файлов </b></p></h2>
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="filename"><br>
<input type="submit" value="Загрузить"><br>
</form>
</body>
</html>[/code]
[b]upload.php[/b]
[code]<html>
<head>
<title>Результат загрузки файла</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
<?php
if($_FILES["filename"]["size"] > 1024*1024*5)
{
echo ("Размер файла превышает 5 МБ");
exit;
}
// Проверяем загружен ли файл
if(is_uploaded_file($_FILES["filename"]["tmp_name"]))
{
// Если файл загружен успешно, перемещаем его
// из временной директории в конечную
$folder = '/home/user/uploadfolder/'; //папка для загрузки файла
$link = 'http://example.com/uploads/'; //её расположение в адресе сайта. Просто для вывода пользователю.
$ext = strtolower(substr(strrchr($_FILES['filename']['name'],'.'), 1)); //расширение файла. Кто знает лучший способ узнать расширение?
$md5filename = md5_file($_FILES["filename"]["tmp_name"]); // проверка md5-суммы загружаемого файла
move_uploaded_file($_FILES["filename"]["tmp_name"], $folder.$md5filename.".".$ext); //перемещение с переименованием файла в md5-сумму.
echo $link.$md5filename.".".$ext; //вывод пользователю ссылки на загруженный файл.
} else {
echo("Ошибка загрузки файла");
}

?>
</body>
</html>[/code]
А теперь попробуйте загрузить в форму один и тот же файл пару раз)

Отредактировано Trilby - 16.07.2011
19339
Dimitry
[b]Готовимся к IPv6 - валидация IP адресов[/b]

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

[quote]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;
}[/quote]
Пример использования:
[quote]if($ver=checkip($_SERVER['REMOTE_ADDR'])) echo "IP-Адрес верный и соответствует протоколу IPv$ver";
else echo "IP-Адрес не верный!";[/quote]
Так как существуют сокращённые формы записи IPv6 адреса, возможно нам потребуется узнать его полную форму.
[b]Раскрытие сокращённого IPv6 адреса[/b]: (например раскрываем [color=blue]::1 [/color]- локальный адрес = [color=blue]0000:0000:0000:0000:0000:0000:0000:0001 [/color])

[quote]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);
}[/quote]
19339
Dimitry
[b]Инструкция по переходу с windows-1251 на UTF-8[/b]

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

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

2. [b]Анализ и автоперекодировка.[/b] Перекодируем все файлы с русскоязычными названиями в новую кодировку, а также содержимое для указанных типов файлов.
Для этого я написал не очень сложный скриптик. По умолчанию он выводит файлы которые хочет перекодировать, чтобы выполнить перекодировку нужно нажать на ссылку rename, recode или mbstr set. Последнее в данном случае подставляет "mb_" перед строковыми функциями, делать это нужно только профессионалам!
Закачайте php файл [color=blue]reencoding.php[/color] и запустите его:
[code]<?

function scan($dir) {
global $script;
$d = dir ($dir); if(!$d) return;
while(false!==($f = $d->read())) if($f != "." && $f != ".." && $f!=$script) recode($dir."/".$f);
$d->close();
}

function recode($dir) {
global $from,$to,$types;
$enc=iconv($from,"$to//IGNORE",$dir);
if($enc!=$dir) {
echo "<b>FILENAME:</b> $dir => $enc<br>";
if($_GET[rename]) {rename($dir,$enc); $dir=$enc;}
}
if(is_dir($dir)) {scan($dir); return;}
$type=explode(".",$dir); $type=$type[count($type)-1];
if(!in_array($type,$types)) return;
$content=file_get_contents($dir);
$econtent=iconv($from,"$to//IGNORE",$content);
if($econtent!=$content) {
echo "<b>CONTENT:</b> $dir<br>";
if($_GET[recode]) {file_put_contents($dir,$econtent);$content=$econtent;}
}
$mblog=""; $a=explode("\n",$content);

for($i=0;$i<count($a);$i++) {
$a1 = preg_replace('/(\W{1})(strlen|strpos|strrpos|substr|strtolower|strtoupper|stripos|strripos|strstr|stristr|strrchr|substr_count)\(/' ,'$1mb_$2(',$a[$i]);
if($a[$i]!=$a1) $mblog.="<span style='color:gray; font-size:11px;'>".($i+1).": ".htmlspecialchars($a[$i])."</span><br>";
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>";
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>";
}

if($mblog) echo "<b>MBSTR CHECK</b>: $dir<br>$mblog";
if($_GET[mbstrset] && $mblog) {
$content = preg_replace('/([[:^alpha:]]{1})(strlen|strpos|strrpos|substr|strtolower|strtoupper|stripos|strripos|strstr|stristr|strrchr|substr_count)\(/' ,'$1mb_$2(',$content);
file_put_contents($dir,$content);
}
}

$script="reencoding.php";
$from="windows-1251";
$to="utf-8";
$types=array("php","html"); //file types to encode content
$dir="./";
scan($dir);

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>";
if($_GET[rename]) echo "<b>RENAME COMPLETE</b><br>";
if($_GET[recode]) echo "<b>RECODE COMPLETE</b><br>";
if($_GET[mbstrset]) echo "<b>MBSTR SET COMPLETE</b><br>";

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

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

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

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

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

[quote]function checkddos($sec) {
$ban=0; $file="protect.dat"; $time=time(); $ip=$_SERVER[REMOTE_ADDR];
$whitelist=array("[color=blue]127.0.0.1[/color]","[color=blue]91.42.*.*[/color]","[color=blue]217.235.*.*[/color]","[color=blue]213.180.*.*[/color]","[color=blue]87.250.*.*[/color]","[color=blue]77.88.*.*[/color]","[color=blue]66.249.*.*[/color]","[color=blue]188.72.*.*[/color]");
$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([color=red][b]1[/b][/color])) die("503 Service not available");[/quote]
19339
Dimitry
[b]Получение информации о состоянии Shoutcast 2 сервера, название песни и так далее[/b]

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

[code]<?
//SHOUTCAST 2 parsing
function shoutcast2($server,$port,$sid=1) {
function filter($str) {return htmlspecialchars($str);}
$info=array();
$info[port]=$port;
$info[server]="ShoutCast2";
//get html
$html=req("http://$server:$port/index.html?sid=$sid","",0,3,0);
//parse
$a=explode('valign="top">',$html);
$a[0]="";$end=explode("</b>",$a[count($a)-1]); $a[count($a)-1]=$end[0];
//filter
for($i=1;$i<count($a);$i++) {
$a[$i]=str_replace('<font class="default">','',$a[$i]);
$x=explode(': ',$a[$i]); $name=$x[0];
$var=strip_tags($x[1]); //clear tags
//set my vars
if($name=="Server Status") {if(substr_count($var,"Server is currently up")) $info[online]=1;}
//Stream is up at 128 kbps with 0 of 10 listeners (0 unique)
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); }
if($name=="Listener Peak") $info[plisteners]=filter($var);
if($name=="Stream Name") $info[djname]=mb_substr(filter($var),0,50);
if($name=="Current Song") $info[song]=filter($var);
if($name=="Stream Genre") $info[genre]=filter($var);
if($name=="Stream ICQ") $info[icq]=filter($var);
if($name=="Stream AIM") $info[aim]=filter($var);
if($name=="Stream URL") $info[url]=filter($var);
}
return $info;
}

//Пример использования функции:
$info=shoutcast2("radioserver.ru",8000,1);
print_r($info);

?>[/code]
В качестве бонуса, пример конфига сервера sc_serv.conf:
[quote]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[/quote]
Ну и самая новая версия Shutcast 2 севрера:
http://imgs.su/users/1/1390817248.zip
19339
Dimitry
[b]Приводим старый PHP код с <? в порядок[/b]

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

[quote]cd /home/user/website.ru
egrep -s -R -e '<\?[e=[:space:]]{1}' -e '<\?$' ./[/quote]
[b]Совет на будущее[/b]

Хотите без проблем и эскейпов вставлять HTML код с PHP переменными прямо в php скрипт, воспользуйтесь способом [b]hededoc[/b].
Пример с вставкой простых переменных и элементов массива:
[code]<?php

$number=15;
$a=array('name'=>'Vasya','years'=>25);

//тут вставляем HTML код
echo <<<HTML

Тут можно писать любые кавычки, например "эти" или 'эти'.
Эй, привет {$a['name']}, как дела?
Твой номер $number, да?

HTML;

?>[/code]

Неавторизованные и новички не могут отправлять сообщения.

© 2008-2020
Контакты | Группа | Privacy и Cookie | Правила