Archive for the ‘Скрипты’ Category

Профилирование и отладка PHP скриптов

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

Read On…

Вложенные SQL запросы: одновременное добавление одинарных и множественных строк в INSERT

Понадобилось для одной задачи сделать выборку из таблицы А по определенным условиям и вставить ее в таблицу B.
Можно особо не заморачиваться, сделать SELECT  в переменную, потом пройтись циклом по INSERT и все.

Но это не очень красивое решение. Очень запросов много + на переменные память расходуется. Проще использовать вложенные запросы, они же nested queries. Но тут возникает проблемка —  если вставлять  только данные из INSERT, то все ок, но если добавлять еще и какие-нибудь неизменные значения, то выдается Subquery returned more than 1 value.

Чтобы не обьяснять на пальцах — сама таблица B

CREATE TEMPORARY TABLE `B` (
  `status` TINYINT(4) NULL DEFAULT NULL ,
  `pair` TEXT NULL DEFAULT NULL ,
)
ENGINE = MyISAM
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;

Копируем из таблицы A и вставляем в B так

INSERT INTO `B` (`pair`) 
            SELECT `pair` FROM `A` ORDER BY RAND() LIMIT 100;

Если поле status имеет неизменное значение, например 123, то просто вот так уже не вставить

INSERT INTO `B` (`status`, `pair`) VALUES (
            (SELECT `pair` FROM `A` ORDER BY RAND() LIMIT 100),
            123);

Не совсем красивое решение нашел

UPDATE `B` SET `status` = '123' WHERE 1=1;

Но хотелось бы в одну строку все заделать. Отпишитесь плиз в коментах, кто знает, как реализовать.

Класс для работы с сервисами сокращения ссылок

Небольшой класс, возвращает сокращенную ссылку. Можно обращаться как напрямую к конкретному сервису, так и выбранному рандомно через Shorteners->any(). Есть возможность указать прокси.

Работает с

  1. bit.ly
  2. goo.gl
  3. is.gd
  4. tinyurl.com
  5. dlmn.org
  6. ndurl.com
  7. qr.cx
  8. gatorurl.com
  9. jmb.tw
  10. linkee.com
  11. mtny.mobi
  12. cli.gs
<?php

error_reporting(1);

class Shorteners {

    public $proxy = null;

    public function bitly($url) {
        $connectURL = 'http://api.bit.ly/v3/shorten?login=masstest&apiKey=R_44403eb439622dd59ba2598255f30824&uri=' . urlencode($url) . '&format=txt';
        return $this->curl_get_result($connectURL, NULL);
    }

    public function googl($url) {
        $connectURL = 'http://goo.gl/api/shorten';
        $post_fields = "security_token=null&url=" . urlencode($url);
        $res = $this->curl_get_result($connectURL, $post_fields);
        $obj = json_decode($res);
        $ans = (!empty($res)) ? $obj->{'short_url'} : false;
        return $ans;
    }

    public function isgd($url) {
        $connectURL = 'http://is.gd/api.php?longurl=' . urlencode($url);
        return $this->curl_get_result($connectURL, NULL);
    }

    public function tinyurl($url) {
        $connectURL = 'http://tinyurl.com/api-create.php?url=' . urlencode($url);
        return $this->curl_get_result($connectURL, NULL);
    }

    public function dlmn($url) {
        $connectURL = 'http://dlmn.org/submit/?url=' . urlencode($url) . '&ajax=false';
        $page = $this->curl_get_result($connectURL, NULL);
        preg_match("/<input id=\"dlmn-loc\" value=\"(.*?)\"/si", $page, $out);
        if (strlen($out[1]) < 3) {
            return false;
        } else {
            $answer = (empty($out[1])) ? false : "http://" . $out[1];
            return $answer;
        }
    }

    public function crum($url) {
        $connectURL = 'http://crum.bs/api.php?function=simpleshorten&url=' . urlencode($url);
        return $this->curl_get_result($connectURL, NULL);
    }

    public function ndurl($url) {
        $connectURL = 'http://www.ndurl.com/api.generate/?url=' . urlencode($url) . '&type=web';
        $res = $this->curl_get_result($connectURL, NULL);
        $obj = json_decode(stripslashes($res));
        $obj = $obj->data;
        $ans = (!empty($res)) ? $obj->{'shortURL'} : false;
        return $ans;
    }

    public function qr($url) {
        $connectURL = 'http://qr.cx/api/?longurl=' . urlencode($url);
        return $this->curl_get_result($connectURL, NULL);
    }

    public function gatorurl($url) {
        $connectURL = 'http://gatorurl.com/api/rest.php?url=' . urlencode($url);
        return $this->curl_get_result($connectURL, NULL);
    }

    public function jmb($url) {
        $connectURL = 'http://jmb.tw/api/create/?newurl=' . urlencode($url);
        return $this->curl_get_result($connectURL, NULL);
    }

    public function linkee($url) {
        $connectURL = 'http://api.linkee.com/1.0/shorten?input=' . urlencode($url);
        $res = $this->curl_get_result($connectURL, NULL);
        $obj = json_decode($res);
        $ans = (!empty($res)) ? $obj->{'result'} : false;
        return $ans;
    }

    public function mtny($url) {
        $connectURL = 'http://mtny.mobi/api/?url=' . urlencode($url) . '&ismobile=false&type=simple';
        return $this->curl_get_result($connectURL, NULL);
    }

    public function cligs($url) {
        $connectURL = 'http://cli.gs/cligs/new';
        $post = "URL=" . urlencode($url);
        $page = $this->curl_get_result($connectURL, $post);
        preg_match("/Your new clig has been created/si", $page, $out);
        if (strlen($out[0]) < 3) {
            return false;
        } else {
            preg_match("#http://twitter.com/home\?status=(.*?)\"#si", $page, $out);
            $answer = (empty($out[1])) ? false : $out[1];
            return $answer;
        }
    }

    public function any($url) {
        $func = get_class_methods($this);
        $sh = $func[array_rand($func, 1)];
        return $this->$sh($url, NULL);
    }

    private function curl_get_result($url, $postdata) {
        $ch = curl_init();
        $timeout = 5;
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        if (isset($postdata)) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
        }
        if ($this->proxy) {
            //var_dump( $this->proxy );
            curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, "60");
            curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
            curl_setopt($ch, CURLOPT_TIMEOUT, "60");
        } else {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, "30");
            curl_setopt($ch, CURLOPT_TIMEOUT, "30");
        }
        $data = curl_exec($ch);
        curl_close($ch);

        return trim($data);
    }

}

$test = new Shorteners();
$url = "http://rambler.ru";
$class_methods = get_class_methods('Shorteners');
foreach ($class_methods as $method):
    echo $test->$method . "\r\n";
endforeach;
?>

Простой способ проверки процесса на существование

Для простых многопоточных систем очень часто используют запуск в фоновом режиме.  Запускают их примерно вот так:

php script.php &

И управление процессом от юзера уходит, отловить такой процесс из другого скрипта уже не получится. А если процесс выполняет какую то одну и ту же задачу с интервалом по крону, и нужно, чтобы при запуска нового процесса старый был убит? Поймать его можно аццкой смесью bash + php:

$killPid = exec("ps ux | awk '/script_name.php/ && !/awk/ {print $2}'");//находим пид процесса
      if($killPid)//если есть, киляем
	  exec ("kill ".$killPid);
exec('php script_name.php &');//запускаем новый

Примеры использования strace для отладки скриптов

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

Пример использования — есть один сервис, который я написал, и обьявилась там проблема — каким-то неведомым образом права на файл, куда пишется лог, начали выставляться криво. А именно: вместо положенных rw — rw — rw стали w — wx — wT. Проблема тут в том, что надо указывать права при смене не как:

chmod('file.txt', '0666'); или
chmod('file.txt', 666);

в этом случае 0666 будет string, а вот так:

chmod('file.txt', intval('666', 8));

Так они уже будут в корректной форме.

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

strace php ./script.php 2>&1 | grep file.txt

Ответ будет вида:

open("/path/to/file.txt", O_RDWR|O_CREAT|O_APPEND|O_LARGEFILE, 0666) = 4
chmod("/path/to/file.txt", 0666) = 0

Вот так можно отмониторить все обращения к файлу
Read On…

Размножение строк — функция генерации всех возможных значений из шаблона {}

Понадобилась такая функция, решил размять мозги. Функция понимает в качестве разделителя запятые, |,  ;. Вызов показан в конце.

<?php

function mashUp($str){
preg_match_all("/{(.*?)}/", $str, $out);
$headArr = $out[1];

for($i = 0; $i < sizeof($headArr); $i++)
{
	$valArr = preg_split("/(\||,|;|	)/", $headArr[$i]);
	($i == 0)? $sTotal = count($valArr) : $sTotal = $sTotal * count($valArr);
	$varr[] = $valArr;
}
for($i = 0; $i < $sTotal; $i++){
	$strings[$i] = $str;
}
foreach($headArr as $num => $val):
	$currPos = 0;	//позиция ключа в $varr
		for($i = 0; $i < $sTotal; $i++)
		{
			if($i == 0):
					if($num == 0)
					{
						$sChange = ( $sTotal / ( count($varr[$num]) ) );
					}
						else
					{
						($old)? $sChange = $old : $sChange = $sChange;
 						$sChange = ($sChange / (count($varr[$num])));
					}

					if(count($varr[$num]) < 2){
						$old = $sChange;
						$sChange = $sTotal;
						}
					if($num == (count($headArr) - 1))
					{
						$sChange = 1;
					}
					$sNum = $sChange;
			endif;

			if($sNum == 0){
					$sNum = $sChange - 1;
					$currPos++;
				} else {
					$sNum--;
				//$currPos++;
				}

			if($currPos > (count($varr[$num]) - 1))
			$currPos = 0;
			preg_match("/{(.*?)}/", $strings[$i], $res, PREG_OFFSET_CAPTURE, 0);
			$strings[$i] = preg_replace("!".preg_quote($res[0][0])."!si", $varr[$num][$currPos], $strings[$i]);
		}
endforeach;
return($strings);
}
$str = "some {1a|2a,3a;4a	5a} strings {1b|2b|3b} that {1c} need to {1d|2d} post {test|example|primer}";
echo maashUp($str);
?>

MYSQL RAND(), как получить рандомную строку из БД

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

Есть таблица, в ней N тысяч записей, нужно вытащить одну рандомно, кой-чего с ней сделать, и если все ок то ее использовать, если нет то вернуть на место с соответствующей отметкой и взять другую, пока не будет найдена подходящая по условиям.
Я, особо не мудря, делаю цикл while до условия, пока не найдена подходящая запись. В цикле такой запрос к БД

SELECT * FROM `table` WHERE 1 ORDER BY RAND() LIMIT 1

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

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

//запрос макс количества записей в переменную
SELECT FLOOR(RAND() * COUNT(*)) AS `offset` FROM `table`

//далее запрос с этой переменной
SELECT * FROM `table` LIMIT $somevar, 1

Он же по тестам получился самый быстрый.

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

SELECT * FROM `table` WHERE id >= (SELECT FLOOR( MAX( id ) * RAND( ) ) FROM `proxy` ) ORDER BY id LIMIT 1

Все варианты расписаны тут.

Единственное, что не устроило во втором варианте, это два запроса к базе.

Подстановка ссылок в текст в процентном соотношении.

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

Переменные вызова:
$text, сам текст в который пихаются ссылки. string
$prc, int, процент плотности ссылок.
$links, array, массив со ссылками вида http://site.ru
$nobr, bool, 1 или 0. Если, например, текста 500кб а ссылок 3, указывает, обрубать ли текст после последней ссылки(1) или оставить все 500кб с 3 ссылками.

Листинг:

function AddLinksToText($text, $prc, $links, $nobr){
	$pos = 0;
	$string = "";
	$totalLinks = count($links); 
	$Words = explode(" ", $text); 
	$totalWords = count($Words);
	$needText = $totalLinks * $prc;

	if($totalWords < $needText){
			$prc = floor($totalWords / $totalLinks); //calculate recommended percentage if haven't enough text
			echo @date("r")." not enough text, decrease density to ".$prc."%\r\n";
			$needText = $totalLinks * $prc;
		} else {
			echo @date("r")." working with text, density is ".$prc."%\r\n";
		}
	foreach($links as $num=>$lnk){
					$pos += $prc;
					$Words[$pos] = "<a href=\"".$lnk."\">".$Words[$pos]."</a>";
				}
	if(!isset($nobr)){				
		$tmp = array_slice($Words, 0, $needText + 3);	
		} else {
		$tmp = $Words;
		}

	//return string
	foreach($tmp as $somevar){
		$string .= $somevar." "; 
	}
	return $string;	

}

Заливка файлов на радикал PHP

Если нет желания забивать сервер картинками, то данный скриптец поможет. Он грузит картинку с веба (урл) на радикал, возвращает адрес картинки на радикале.

$postdata = "upload=yes&F=&URLF=".$imagelink."&O=yes&M=640&JQ=85&J=yes&IM=7&VM=180&R=0&VE=yes&V=Увеличить&X=&FS=";
$page = FetchUrl("http://www.radikal.ru/action.aspx", $postdata, NULL, NULL, NULL);
preg_match("!id=\"input_link_1\" value=\"(.*?)\"!si", $page, $imageUrl);
$imageUrl = $imageUrl[1];

FetchUrl моя универсальная функция для курла, я ее под все подряд юзаю, поэтому там много левого кода. При желании можно поудалять половину.

function FetchUrl($url, $postvars, $timeout, $ref, $blank){
 sleep($timeout);                         
 echo @date("r")." fetching $url\r\n";              
 $ch = curl_init();   
 if(isset($postvars)){
 echo @date("r")." $postvars\r\n";                    
 curl_setopt($ch, CURLOPT_POST, 1); 
 curl_setopt($ch, CURLOPT_POSTFIELDS, $postvars); 
 }
//        curl_setopt($ch, CURLOPT_PROXY, $proxy);
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);       
//        curl_setopt($ch, CURLOPT_HEADER, true);           
 curl_setopt($ch, CURLOPT_TIMEOUT,15);               
 curl_setopt($ch, CURLOPT_ENCODING, 'gzip');         
 curl_setopt($ch, CURLOPT_COOKIEJAR, "c1.txt");    
 curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.6) Gecko/20091201 SUSE/3.5.6-1.1.1 Firefox/3.5.6 FirePHP/0.3");     
 curl_setopt($ch, CURLOPT_COOKIEFILE, "c1.txt");     
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);  
 curl_setopt($ch, CURLOPT_URL, trim($url));
 curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE); 
 curl_setopt($ch, CURLOPT_REFERER, $ref);                                                                  
 $result = curl_exec($ch);     
 if($blank == "1"){    
     $result = preg_replace("/\n/", "", $result);
     $result = preg_replace("/\r/", "", $result);
 } 
 curl_close($ch);                                                                                        
 return $result;                                                                                              
 } 

Многопоточность в php

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

Встала вобщем задача передо мной — накатать многопоточный нескажу что, причем не просто многопоточный, а чтобы еще и потоками можно было управлять, как в том же делфи. То есть, чтобы можно было поток остановить, запустить, запауазить, возобновить и потоки еще должны оповещать о своем состоянии, мол в данный момент выполняю то-то. Read On…

css.php