Лис 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)

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

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

Чер 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 скриптом. Я використовую цей скрипт для захисту скріншотів фільмів і отримую наступний результат.

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

Чер 07 2012
 

В попередньо реалізованому класі , котрий забезпечує  життєвий цикл гри, ми не мали можливості задавати потрібний нам FPS. Сьогодні ми будемо вносити в цей цикл зміни для досягнення максимальної ефективності ігрового циклу.

Період ітерації

Період ітерації – час на протязі якого відбувається одна ітерація в ігровому циклі. Наприклад, якщо нам потрібен FPS  50,  то період ітерації повинен бути рівним 20 мілісекунд ( 1 секунда / 50 = 1000 мілісекунд / 50 =  20 мілісекунд). Для забезпечення заданого та сталого FPS, кожна ітерація циклу повинна тривати не більше і не менше ніж значення періоду однієї ітерації. Оптимізуємо клас  додавши можливість задання fps через конструктор класу ( public Game(int fps) ). Конструктор тепер приймає ціле число (fps) і відносно цього числа розраховує необхідний період ітерації (LOOP_PERIOD).




public class Game {

   long startTime;
   int FPS;
   int countFrames;

   int LOOP_PERIOD; // період ітерації

   public Game(int fps){
      //визначаємо період для одної ітерації
      LOOP_PERIOD = (int)1000/fps;
      startTime = System.nanoTime(); // повертає значення в наносекундах, тип long
   }

   public void update(){
   //Оновлення стану
   }

   public void render(){
   //Перемальовка графіки
   countFrames++; // фрейм відрендерили
   }

   public void checkFps(){
      long currentTime = System.nanoTime(); // повертає значення в наносекундах, тип long
      FPS = countFrames / (int) (currentTime/1000000000L - startTime/1000000000L); // виміряли FPS
      System.out.println("FPS: "+FPS);
   }

   public void loop(){
      while(true){
         //Оновлення стану
         update();
         //Перемальовка графіки
         render();
         //Пауза
         try {
            Thread.sleep(100);
         } cath(InterruptedException e){
            System.out.println(e.getMessage());
         }
        if(countFrames % 1000 == 0){
            checkFps();
        }
      }
   }

   public static void main(String[] args){
      Game game = new Game(50);
      game.loop(); //запуск гри
   }

}



Беремо цикл під контроль

Погляньте на наступний шматок коду




//Оновлення стану
update();
//Перемальовка графіки
render();
//Пауза
try {
  Thread.sleep(100);
} cath(InterruptedException e){
  System.out.println(e.getMessage());
}



Як ви можете побачити, гра ставиться на паузу тривалістю 100 мілісекунд в кінці кожної ітерації. Чому пауза саме на 100 мілісекунд? Максимальний fps при такій паузі не може перевищити 10  (1000 / 100). Але я скажу більше, він буде меншим ніж 10, тому що методи update  та  render  також виконуються певний час. Якщо все врахувати то fps вийде рівним  1000 / (100 + час роботи update + час роботи render) . Скільки часу давати на паузу? Час паузи  будемо розраховувати з наступного рівняння

sleepTime = LOOP_PERIOD – (час роботи update + час роботи render)

Оптимізуємо код для врахування усіх необхідних величин




public class Game {

   long startTime;
   int FPS;
   int countFrames;

   int LOOP_PERIOD; // період ітерації

   public Game(int fps){
      //визначаємо період для одного циклу гри
      LOOP_PERIOD = (int)1000/fps;
      startTime = System.nanoTime(); // повертає значення в наносекундах, тип long
   }

   public void update(){
   //Оновлення стану
   }

   public void render(){
   //Перемальовка графіки
   countFrames++; // фрейм відрендерили
   }

   public void checkFps(){
      long currentTime = System.nanoTime(); // повертає значення в наносекундах, тип long
      FPS = countFrames / (int) (currentTime/1000000000L - startTime/1000000000L); // виміряли FPS
      System.out.println("FPS: "+FPS);
   }

   public void loop(){
      long beforeCycle; // час до виклику  update та render
      long afterCycle; // час після update та render
      long sleepTime;
      while(true){
         beforeCycle = System.nanoTime(); // засікаємо час до

         //Оновлення стану
         update();
         //Перемальовка графіки
         render();

         afterCycle = System.nanoTime(); // засікаємо час після

         sleepTime = (LOOP_PERIOD*1000000) - (afterCycle - beforeCycle); // вираховуємо час для паузи

         //Пауза
         if(sleepTime > 0) {
            try {
                  Thread.sleep(sleepTime/1000000L); // наносекунди в мілісекунди
                } catch (InterruptedException ex) {
                  System.err.println(ex.getMessage());
                }
        }
        if(countFrames % 1000 == 0){
            checkFps();
        }
      }
   }

   public static void main(String[] args){
      Game game = new Game(50); // задаємо потрібний нам fps
      game.loop(); //запуск гри
   }

}



beforeCycle – тип long, afterCycle – тип long, а LOOP_PERIOD – тип int, тому ми маємо перевести LOOP_PERIOD в тип long помноживши його на 1000000

величину sleepTime ми отримуємо в наносекундах, а функція Thread.sleep приймає величину в мілісекундах. Нам потрібно перевести наносекунди в мілісекунди розділивши sleepTime на 1000000L. Буква L потрібна щоб вказати що це тип long.

Суть оптимізації в тому, що ми  в кожній ітерації динамічно вираховуємо необхідний sleepTime. Наприклад, якщо update та render виконувалися 10 мілісекунд а період ітерації 50 мілісекунд, то sleepTime = (50 – 10) = 40 мілісекунд.   Такий підхід забезпечує стабільний fps котрий задається  при запуску програми.

Тестуємо цикл

Як бачите, ми змогли досягнути потрібного FPS. Зверніть увагу на те,  що  інколи fps є рівним 51. Це пов’язано з похибкою яка присутня у функціях вимірювання часу і та у функції паузи. Ця похибка є незначною і не буде негативно впливати на роботу програми.

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

Кві 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 для збереження даних.

Вер 23 2010
 
1bffe6b48421db7bfd3ea11d86c

MySQL Query Browser це графічний інструмент, що надається MySQL AB для створення, виконання та оптимізації запитів в графічному середовищі. Тоді як MySQL Administrator розроблений для адміністрування MySQL сервера, MySQL Query Browser створено допомогти Вам отримувати і аналізувати дані, що зберігаються у Вашій MySQL базі. MySQL Query Browser розроблений для роботи з MySQL версії 4.0 та вище.

Всі запити, які виконуються в MySQL Query Browser, так само можуть бути виконані в консольному додатку mysql, MySQL Query Browser дозволяє отримувати та редагувати дані більш інтуїтивно зрозумілим графічним способом.

MySQL Query Browser це додаток, створений завдяки  відгукам, які MySQL AB отримувала від користувачів протягом кількох років.

Що мені найбільше сподобалось у MySQL Query Browser, так це приємний графічний інтерфейс, підсвітка коду яка запобігає синтаксичним помилкам при введені команд, простота запуску, результати запитів відображаються у окремих вкладках (так як у веб браузерах) що дозволяє переключатися між результатами не закриваючи їх. Також у MySQL Query Browser є довідка по функціям, яка допоможе у разі коли ви забули синтаксис якоїсь команди.  Довідку можна знайти у правому нижньому вікні.  Єдиний мінус це англійська мова інтерфейсу, хоча  все зрозуміло навіть на англійські.

Скачати MySQL Query Browser для віндовс можна тут.