Рубрика архива: PHP

RegExp

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

упрощенный макрос link{«text»,id} и полная версия link{«text»,id}img{id}, результат их работы будет таков
макрос номер 1:
<a href="link_id">text</a>
макрос номер 2:
<a href="link_id">text<img src="img_id"></a>

регулярное выражение для полной версии макроса
/link\{«(.*?)»,([0-9]+)?)\}img\{([0-9]+?)\}/
и соответственно для упрощенной
/link\{«(.*?)»,([0-9]+?)\}/

пример шаблона:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed arcu link{«lorem»,10}img{20}, semper id dictum sit amet, egestas a dui. Donec condimentum link{«gravida ullamcorper»,5}.

результат работы регулярного выражения для полной версии макроса lorem, 10, 20
результат работы для упрощенной: lorem»,10}img{20}, semper id dictum sit amet, egestas a dui. Donec condimentum link{«gravida ullamcorper, 5

Прямо скажем, не то что я ожидал увидеть. Пробовал разные варианты ограничить жадность квантификаторов, даже всякие магически пасы в виде ?!, ?!= и прочих, пока без просвета.

regexp и парные скобки

Из строки «The {World {is}} beautifull» нужно достать максимальную подстроку ограниченную парными скобками.
Решение:

$string="The {World {is}} beautifull";
if (preg_match("#\{((?>[^{}]+)|(?R))*\}#", $string,$m)){
    print "matched: ".$m[0];
}

REGex и экранированные символы

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

// сам regexp
// /"(?>\\"|.)*?"/

$pattern= '/"(?>\\"|.)*?"/';
$string = 'some "text \"word\""';
if (preg_match($pattern,$string)){
    print "TRUE";
}

Yii, первые шаги

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

в классе CWebApplication, есть публичное свойство defaultController, по умолчанию равное site, задать новое значение можно в файле конфигурации. Примерно так:

//config/main.php
return array(
	//...
    'defaultController'=>'otherControler',
	//...
}

пишем галерею, часть 2.

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

пишем галерею, часть 1.

Сколько всего уже успели понаписать в нашем мире, есть и большие навороченные галереи, есть и маленькие, есть прекрасные а есть не очень. Есть галереи с использованием БД, а есть нет. Казалось, выбирай и радуйся. Но это просто, а значит не интересно. Будем писать галерею с использованием ООП, на ПХП :), без БД, даже практически без файлов), все будет хранится в объектах, которые в промежутках между вызовами будут сериализованы и аккуратно записаны на жесткий диск.
Почему так?, потому, что такой вариант хранения данных я еще не использовал, практика 🙂

Так как создание галереи есть процесс настоящего времени, то логика и модель архитектуры еще весьма не однозначна, что-то будет добавлено, что-то убрано, а что-то изменено до неузнаваемости, отнеситесь с пониманием к этому факту 🙂
Подробнее »

класс SimpleFeedBack

intro
Иногда возникает задача вставить на страницу форму обратной связи, ну или вообще какую нибудь форму для отправки информации на почтовый ящик. И вот решил я это дело несколько автоматизировать. Создал простой класс который позволяет создавать форму и отправлять данные из нее мне на почту. Ключевые возможности, настраиваемый стиль каждого поля, возможность передачи вашему скрипту для дальнейшей обработки формы, редирект на вашу страниц после отправки, и возвращение результата прямиком на страницу с формой ajax’ом.
Подробнее »

постраничная разбивка контента

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

Я встречал пока только два основных направления «пейджификации», это постраничная разбивка, для которой требуется в обязательном порядке знать общее число
страниц. И разбивка вида «ВПЕРЕД», «НАЗАД», для ее реализации достаточно немного изменить сам запрос дерганья контента из БД.
В случае постраничного отображения, мы получаем более наглядный и удобный способ визуализации, который легко можно представить как второй способ.
Но за все приходится платить, в данном случае дополнительным запросом к БД.
Второй вариант реализован путем получения ответа от Бд, составляющей страницу на 1 элемент больше чем собирались выводить, если этот элемент
присутствует то значит мы можем дать ссылку на последующую страницу. Иногда, где не требуется вся мощь и красота постраничного вывода, этот способ тоже применим.

пример реализации:

вариант «< НАЗАД | ВПЕРЕД >»

$start=isset($_GET['page']) ? $_GET['page'] : 0;	// с какого элемента начинаем забирать данные
$pages=15;											// количество выводимых полей из бд

$result=mysql_query("SELECT * FROM `table` ORDER BY `id` LIMIT $start,($pages+1)");

// код вывода ссылки если возможна навигация назад
$prev=($start-$pages>=0) ? '<a href="page.php?page='.($start-$pages).'"> BACK </a>' : 'BACK';
// если возможна навигация вперед
$next=count($result)>$pages ? '<a href="page.php?page='.($start+$pages).'"> NEXT </a>' : 'NEXT';

print $prev.' | '.$next;

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

if (((int)$start % (int)$pages)!=0){print '404, Sorry, page not found';exit;}

вариант «1 2 3 4 [5] 6 7 8»

$max=mysql_query("SELECT COUNT(*) FROM `table`"); // количество записей в таблице
$pages=15; // по сколько выводить записей на страницу
$page=$_GET['page']; // текущая страница

for ($i=1;$i<=ceil($max/$pages);$i++){
    print ($i==$page) ? ' <span style="color : #262626;	font-size : 0.82em;	font-weight : bold;">[ '.$i.' ]</span> ': ' <a style="color : #C2C2C2;	font-size : 0.72em;" href="#page.php?page='.$i.'">'.$i.'</a> ';
}

результат:
1 2 3 4 5 6 7 8 9 10 11 12 13 [ 14 ] 15 16 17 18 19 20 21 22 23 24 25

$max=mysql_query("SELECT COUNT(*) FROM `table`"); // количество записей в таблице
$pages=15; // по сколько выводить записей на страницу
$page=$_GET['page']; // текущая страница
$len=5; // количество отображаемых ссылок слева и справа от текущей страницы

//left
$left=$len>=$page-1? $page-1:$len;
for ($i=0;$i<=$left-1;$i++){

    print ' <a style="color : #C2C2C2;	font-size : 0.72em;" href="#page.php?page='.($page+$i-$left).'">'.($page+$i-$left).'</a> ';
}

print '<span style="color : #262626;	font-size : 0.82em;	font-weight : bold;"> [ '.$page.' ] </span>';

//right
$right=ceil($max/$pages)<=$page+$len?ceil($max/$pages)-$page:$len;
for ($i=1;$i<=$right;$i++){

    print ' <a style="color : #C2C2C2;	font-size : 0.72em;" href="#page.php?page='.($page+$i).'">'.($page+$i).'</a> ';
}

результат:
9 10 11 12 13 [ 14 ] 15 16 17 18 19

В качестве max, page, pages, len можно поставить любые цифры и посмотреть на итоговый результат.

кэширование запросов

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

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

в идеале это сводится к следующему:

$query='SELECT * FROM `table` ORDER BY id DESC LIMIT 1';
$rez=$cache->get('GET_LAST_POST',$query);

но, это требует от класса еще научится работать с БД, что непременно его усложнит.
поэтому вот модель попроще:

$query='SELECT * FROM `table` ORDER BY id DESC LIMIT 1';
$rez=$cache->is('GET_LAST_POST') ? $cache->get('GET_LAST_POST') : mysql_query($query);
$cache->put($rez);

Уже не так красиво, как в первом варианте, зато не требует от будущего класса функций сверх необходимых.

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

class Cache {
    
    private $path=''; # путь до папки с кэшем
    private $str='';   # имя файла кэша
    private $isStr=false; #есть ли имя
    private $isCache=false; #есть ли файл по этому имени

# устанавливаем путь
    public function setPath($path){
        $this->path=$path;
    }
# передаем имя
    public function setStr($str){
        $str=trim($str);
        if (strlen($str)<=2){
            return false;
        }else{
            $this->str=$this->prepareStr($str);
            $this->isStr=true;
            $this->isCache=($this->isFile($this->str))?true:false;            
            return true;
        }
    }
# возвращаем сгенерированое имя, имя конкретного файла кэша
    public function getStr(){
        return $this->str;
    }
# возвращаем кэш, если есть что
    public function getCache(){
        if ($this->isCache==true){
            return file_get_contents($this->path.'/'.$this->str);
        }else{
            return '';
        }
    }
# есть ли кэш
    public function isCache(){
        return $this->isCache;
    }
# записать в кэш
    public function putToCache($str){
        if ($this->isStr){
            $file = fopen ($this->path.'/'.$this->str,'w');

              if ( !$file )
              {
                $this->isCache=false;
                return false;
              }
              else
              {
                fputs ( $file, $str);
              }
              fclose ($file);
              if ($this->isFile($this->str)==true){
                  $this->isCache=true;
                  return true;
              }else{
                  return false;
              }
        }else{
            return false;
        }
    }
#очстить папку кэша
    public function clearCache(){
        if (empty($this->path) || !file_exists($this->path.'/')){return false;}
        $dir = opendir ($this->path.'/');
          while ( $file = readdir ($dir)){
            if (( $file != ".") && ($file != "..") && strlen($file)==32)
            {
                if (is_file($this->path.'/'.$file)){$this->deleteFile($this->path.'/'.$file);}
            }
          }
          closedir ($dir);
          return true;
    }
#физическое удаление файла
    private function deleteFile($file){
        //print $file;
        unlink($file);
    }
#есть ли файл кэша
    private function isFile($file){
        return (!empty($file) && file_exists($this->path.'/'.$file))?true:false;
    }
#подготавливаем имя файла из переданных параметров
    private function prepareStr($str){        
        return md5(strtolower($str));
    }
}

вроде бы все возможные косяки были учтены, и код работает.

Живой пример использования класса:

include_once('/class/Cache.php');
$cache=new Cache;
$cache->setPath('/www/cache');
#cache_code
    if(isset($cache) && $cache->setStr('GET_NEWS_'.$_GET['id']) && $cache->isCache()){
        $aNews=unserialize($cache->getCache());
    }
    else{
        $aNews=mysql_query('SELECT * FROM `news` WHERE `id`='.$_GET['id'].' LIMIT 1;');
        if (isset($cache)){$cache->putToCache(serialize($aNews));}
    }
#

построение дерева, ч.1

picaTreeChuвот такой код генерирует красивое дерево:

$m=array(1=>1, 2=>0 , 3=>1, 4=>3, 5=>0, 6=>1, 7=>5, 8=>5, 9=>5 );

t($m,0,'..');

function t($m,$id,$pre)
{

	foreach ($m as $key=>$value)
	{
		if ($value==$id)
		{

			print "$pre ".$key."\n";
			t($m,$key,$pre."..");

		}
	}	

}

🙂