<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xlab &#187; Marcin Dryka</title>
	<atom:link href="http://xlab.pl/author/drymek/feed/" rel="self" type="application/rss+xml" />
	<link>http://xlab.pl</link>
	<description>XSolve laboratory - dzielimy się tym co wiemy...</description>
	<lastBuildDate>Thu, 12 Apr 2012 07:23:58 +0000</lastBuildDate>
	<language>pl</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Czy Facebook umożliwia kierowanie pojazdem?</title>
		<link>http://xlab.pl/czy-facebook-umozliwia-kierowanie-pojazdem/</link>
		<comments>http://xlab.pl/czy-facebook-umozliwia-kierowanie-pojazdem/#comments</comments>
		<pubDate>Wed, 10 Feb 2010 08:05:16 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Ogólne]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=883</guid>
		<description><![CDATA[Okazuje się, że tak! Zabawna historia. Wczoraj przechodząc od auta, do miejsca mojej codziennej pracy, wypadło mi prawo jazdy wprost na chodnik. W historii nie byłoby nic nadzwyczajnego &#8211; należałoby tylko zgłosić na policję fakt zagubienia, zapłacić 75zł za wyrobienie nowego dokumentu i koniec sprawy &#8211; gdyby nie to, że miły znalazca, Pan Przemek, znalazł [...]]]></description>
			<content:encoded><![CDATA[<p>Okazuje się, że tak! Zabawna historia. Wczoraj przechodząc od auta, do miejsca mojej codziennej pracy, wypadło mi prawo jazdy wprost na chodnik. W historii nie byłoby nic nadzwyczajnego &#8211; należałoby tylko zgłosić na policję fakt zagubienia, zapłacić 75zł za wyrobienie nowego dokumentu i koniec sprawy &#8211; gdyby nie to, że miły znalazca, Pan Przemek, znalazł również mój profil na Facebooku i zostawił mi wiadomość, że to właśnie on je odnalazł. Zaproponował tym samym, że w dniu dzisiejszym będzie w tym samym miejscu i może mi je przekazać. Tak też się stało, dokument wrócił do mnie cały i bezpieczny, a ja mogę się już nie obawiać kontroli policyjnej. </p>
<p>Znalazcy chciałbym jeszcze raz bardzo podziękować: dziękuję!</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/czy-facebook-umozliwia-kierowanie-pojazdem/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Dlaczego kocham PHP!</title>
		<link>http://xlab.pl/dlaczego-kocham-php/</link>
		<comments>http://xlab.pl/dlaczego-kocham-php/#comments</comments>
		<pubDate>Thu, 19 Nov 2009 17:44:39 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[błąd]]></category>
		<category><![CDATA[fail]]></category>
		<category><![CDATA[love]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=636</guid>
		<description><![CDATA[Każdego programistę w życiu spotykają chwile doszukiwania się logiki lub chwile zwątpienia w zasady matematyki. Język PHP w znaczącym stopniu ułatwia zaistnienie takiej sytuacji. Wyobraźmy sobie, potrzebę napisania klasy umożliwiającej stronicowanie wyników. Metoda calculate służy do policzenia dolnego zakresu wyników (strona 5, wyniki 51-60). Całość wygląda następująco: class Pager { private $page = 1, $per_page [...]]]></description>
			<content:encoded><![CDATA[<p>Każdego programistę w życiu spotykają chwile doszukiwania się logiki lub chwile zwątpienia w zasady matematyki. Język PHP w znaczącym stopniu ułatwia zaistnienie takiej sytuacji.<br />
Wyobraźmy sobie, potrzebę napisania klasy umożliwiającej stronicowanie wyników. Metoda <em>calculate</em> służy do policzenia dolnego zakresu wyników (strona 5, wyniki <strong>51</strong>-60). Całość wygląda następująco:</p>
<pre name="code" class="php">
class Pager
{
  private
    $page = 1,
    $per_page = 10;

  public function calculate()
  {
    return $this-&gt;page * $this-&gt;per_page - $this-per_page + 1;
  }
}</pre>
<p>Jedna mała pomyłka. Zamiast</p>
<pre name="code" class="php">$this-&gt;per_page</pre>
<p>do kodu wkradło się:</p>
<pre name="code" class="php">$this-per_page</pre>
<p>Co to zmienia? Na pierwszy rzut oka parser PHP powinien poinformować o błędzie&#8230; jednak linia kodu zawierająca błąd interpretowana jest następująco:</p>
<pre name="code" class="php">
$this-&gt;page // 1</pre>
<pre name="code" class="php">
$this-&gt;per_page // 10</pre>
<pre name="code" class="php">
$this  // rzutowane na int to 1, bo obiekt istnieje</pre>
<pre name="code" class="php">
per_page // jako stała niezdefiniowana to 0</pre>
<p>Wykonywane działanie matematyczne wygląda zatem następująco:</p>
<pre name="code" class="php">
1 * 10 - 1 - 0 + 1</pre>
<p>Wynik działania to o dziwo 10&#8230; Dlatego kocham PHP!</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/dlaczego-kocham-php/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Demotywacja demotywatorów II</title>
		<link>http://xlab.pl/demotywacja-demotywatorow-ii/</link>
		<comments>http://xlab.pl/demotywacja-demotywatorow-ii/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 16:29:16 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[demotywatory]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rss]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=622</guid>
		<description><![CDATA[&#171; Poprzednia część. Już po krótkim używaniu skryptu, nie trudno się zorientować, że działa on zbyt wolno. Aby przyspieszyć działanie można użyć zachowywania informacji o już ściągniętych demotywatorach. Zatem tekst o tym jak dodać cache: Klasa Feed. Różni się nieznacznie od oryginału &#8211; należy dodać cztery nowe metody: public function addToCache($page_url, $image_url) { Debug::Log("Adding to cache"); [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.xlab.pl/2009/10/demotywacja-demotywatorow/">&laquo; Poprzednia część. </a><br />
Już po krótkim używaniu skryptu, nie trudno się zorientować, że działa on zbyt wolno. Aby przyspieszyć działanie można użyć zachowywania informacji o już ściągniętych demotywatorach. Zatem tekst o tym jak dodać cache:<br />
Klasa Feed. Różni się nieznacznie  od oryginału &#8211; należy dodać cztery nowe metody:</p>
<pre name="code" class="php">
  public function addToCache($page_url, $image_url)
  {
    Debug::Log("Adding to cache");
    $this->cache[$page_url] = $image_url;
  }

  public function loadCache()
  {
    Debug::Log('Reading cache');
    if (file_exists("cache.csv"))
    {
      $fh = fopen("cache.csv", "r");
      while (($data = fgetcsv($fh, 1000, ",")) !== FALSE)
      {
        $this->cache[$data[0]] = $data[1];
      }
      fclose($fh);
      return $this->cache;
    }
  }

  public function saveCache()
  {
    Debug::Log("Saving cache");
    $fh = fopen('cache.csv', 'w');
    foreach ($this->cache as $page_url=>$image_url)
    {
      fputcsv($fh, array($page_url, $image_url));
    }
    fclose($fh);
  }

  public function isCached($page_url)
  {
    Debug::Log("Checking cache");
    if (empty($this->cache))
    {
      $this->loadCache();
    }
    if (isset($this->cache[$page_url]))
    {
      Debug::Log("Found in cache");
      return $this->cache[$page_url];
    }
    Debug::Log("Not found in cache");
    return false;
  }</pre>
<p>oraz użyć ich w odpowiedni sposób. Cały plik wygląda następująco:</p>
<pre name="code" class="php">
<?php
require_once 'Remote.php';        //Read remote files
require_once 'FeedException.php'; //Implementation of Feed Exception
require_once 'Debug.php';         //Debug message

/*
 * Class to convert demotywatory.pl rss
 *
 * @author drymek
 *
 */
class Feed
{
  private
    $url,             //rss.xml file url
    $orginalContent,  //rss.xml file content
    $transformedDom,  //rss.xml file DOMDocument
    $cache = array(); //Keep cache 

  /*
   * Set feed url
   */
  public function setUrl($url)
  {
    $this->url = $url;
  }

  /*
   *  Read remote rss file
   */
  public function getOrginalContent()
  {
    if (is_null($this->url))
    {
      throw new FeedException("You have to set url first!");
    }

    $this->orginalContent = Remote::get_remote_file_content($this->url);
    return $this->orginalContent;
  }

  /*
   * Create a DOMDocument from rss.xml file
   */
  public function getDomOrginalContent()
  {
    if (false === $this->orginalContent || is_null($this->orginalContent))
    {
      throw new FeedException("Can not download rss file");
    }

    $domDocument = new DOMDocument();
    $domDocument->loadXML($this->orginalContent);
    return $domDocument;
  }

  public function addToCache($page_url, $image_url)
  {
    Debug::Log("Adding to cache");
    $this->cache[$page_url] = $image_url;
  }

  public function loadCache()
  {
    Debug::Log('Reading cache');
    if (file_exists("cache.csv"))
    {
      $fh = fopen("cache.csv", "r");
      while (($data = fgetcsv($fh, 1000, ",")) !== FALSE)
      {
        $this->cache[$data[0]] = $data[1];
      }
      fclose($fh);
      return $this->cache;
    }
  }

  public function saveCache()
  {
    Debug::Log("Saving cache");
    $fh = fopen('cache.csv', 'w');
    foreach ($this->cache as $page_url=>$image_url)
    {
      fputcsv($fh, array($page_url, $image_url));
    }
    fclose($fh);
  }

  public function isCached($page_url)
  {
    Debug::Log("Checking cache");
    if (empty($this->cache))
    {
      $this->loadCache();
    }
    if (isset($this->cache[$page_url]))
    {
      Debug::Log("Found in cache");
      return $this->cache[$page_url];
    }
    Debug::Log("Not found in cache");
    return false;
  }
  /*
   * Get image URL from remote webpage
   */
  public function getImage($item)
  {
    if ('link' == $item->nodeName)
    {
      if ($cache = $this->isCached($item->nodeValue))
      {
        return $cache;
      }
      $htmlContent = Remote::get_remote_file_content($item->nodeValue);
      if (false !== $htmlContent)
      {
        //Search image tag
        $pattern = "/bigurl=\".*\"/";
        preg_match($pattern, $htmlContent, $urls);

        //Finally url to the image
        $url = substr($urls[0], 8, -1);
        $this->addToCache($item->nodeValue, $url);
        return $url;
      }
      return 'http://demotywatory.pl/res/img/demotywatory-logo.gif';
    }
  }

  /*
   * Set new description for current item
   */
  public function setImage($item)
  {
    //Find element link
    foreach ($item->childNodes as $rssDomItemChild)
    {
      if ('link' == $rssDomItemChild->nodeName)
      {
        //Download demot page
        $url = $this->getImage($rssDomItemChild);
        break;
      }
    }

    //Find element description
    foreach ($item->childNodes as $rssDomItemChild)
    {
      if ('description' == $rssDomItemChild->nodeName)
      {
        $rssDomItemChild->nodeValue = '&lt;img src="'.$url.'"&gt;';
        break;
      }
    }
    Debug::Log('Found url: ['.$url.']');
  }

  /*
   * Convert rss.xml to rss.xml with images in description
   */
  public function transform()
  {
    //Get DOMDocument
    $this->transformedDom = $this->getDomOrginalContent();
    //Find all items
    $rssItems = $this->transformedDom->getElementsByTagName('item');

    //Foreach feed
    Debug::Log('Starting retriving feeds');

    foreach ($rssItems as $rssItem)
    {
      //Set image in description for node
      $this->setImage($rssItem);
    }
    Debug::Log('Finished retriving feeds!');
  }

  /*
   * Get transformed XML
   */
  public function getResults()
  {
    return $this->transformedDom->saveXml();
  }

  /*
   * Save results to file (xml)
   */
  public function save($filename)
  {
    Debug::Log('Saving file ('.$filename.')');
    $fh = fopen($filename, 'w');
    if (!$fh)
    {
      throw new FeedException('Can not open file ('.$filename.') to write!');
    }
    fwrite($fh, $this->getResults());
    fclose($fh);
  }
}</pre>
<p>Należy jeszcze zmienić skrypt wywołujący, czyli index.php:</p>
<pre name="code" class="php">
<?php
define('DEBUG', true);    //Set debug on
require_once 'Feed.php';  //Include Feed class

//Create new Feed instance
$feed = new Feed();
//Set feed url
$feed->setUrl('http://demotywatory.pl/rss.xml');
//Get rss file content
$feed->getOrginalContent();
//Convert to new format
$feed->transform();
//Save to file
$feed->save('result.xml');
$feed->saveCache();</pre>
<p>I to wszystko. Teraz skrypt pobierze informacje jedynie o nowych rssach. Informacje które już posiada w razie potrzeby zostaną użyte ponownie.<br />
Efekt działania można zobaczyć na <a href="http://drymek.hostei.com/">http://drymek.hostei.com/</a>.<br />
Gotowy do użycia plik rss znajduje się tutaj: <a href="http://feeds.feedburner.com/demotywatorator?format=xml">http://feeds.feedburner.com/demotywatorator?format=xml</a></p>
<p>Zestaw wszystkich plików do ściagnięcia: <a href="http://www.xlab.pl/wp-content/uploads/2009/10/Demotywatorator2.zip">Demotywatorator2</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/demotywacja-demotywatorow-ii/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Demotywacja demotywatorów</title>
		<link>http://xlab.pl/demotywacja-demotywatorow/</link>
		<comments>http://xlab.pl/demotywacja-demotywatorow/#comments</comments>
		<pubDate>Fri, 16 Oct 2009 18:16:14 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[demotywatory]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rss]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=601</guid>
		<description><![CDATA[Demotywatory.pl, znany polski serwis humorystyczny. Jednak serwis ma jedną niedogodność &#8211; trzeba wchodzić na strone internetową aby zobaczyć obrazki. Podobnie w przypadku kanału RSS &#8211; nie ma możliwości obejrzenia obrazka w treści wiadomości. Jest problem jest i fix: 5 plików rozwiązujących problem: Remote.php FeedException.php Debug.php Feed.php index.php Pierwszy z nich, rozbudowany aż nadto co powinien [...]]]></description>
			<content:encoded><![CDATA[<p>Demotywatory.pl, znany polski serwis humorystyczny. Jednak serwis ma jedną niedogodność &#8211; trzeba wchodzić na strone internetową aby zobaczyć obrazki. Podobnie w przypadku kanału RSS &#8211; nie ma możliwości obejrzenia obrazka w treści wiadomości. Jest problem jest i fix:<br />
5 plików rozwiązujących problem:</p>
<ol>
<li>Remote.php</li>
<li>FeedException.php</li>
<li>Debug.php</li>
<li>Feed.php</li>
<li>index.php</li>
</ol>
<p>Pierwszy z nich, rozbudowany aż nadto co powinien być, jednak faza testowania wymagała porównania prędkości działania kilku rozwiązań. Curl został domyślnym, ze względu na jego szybkość i niezawodność.</p>
<pre class="php" name="code">
&lt;?php

/*
 * Read remote file with some methods
 *
 * @author drymek
 *
 */
class Remote
{
  /*
   * Read file with default method
   */
  public static function get_remote_file_content($url)
  {
    return self::get_with_curl($url);
  }

  /*
   * Curl - default method for Remote
   */
  public function get_with_curl($url)
  {
    // create a new cURL resource
    $ch = curl_init();

    // set URL and other appropriate options
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    // grab URL and pass it to the browser
    $content = curl_exec($ch);

    // close cURL resource, and free up system resources
    curl_close($ch);
    return $content;
  }

  /*
   * file_get_contents use buildin PHP function
   */
  public function get_with_file_get_contents($url)
  {
    return file_get_contents($url);
  }

  /*
   * fopen and fgets functions to read file
   */
  public function get_with_fopen($url)
  {
    $fh = fopen($url, 'rb');
    if (!$fh)
    {
      return '';
    }

    $content = '';
    while (!feof($fh))
    {
      $content .= fgets($fh, 1024);
    }
    fclose($fh);
    return $content;
  }
}</pre>
<p>Drugi z plików zapewnia jedynie możliwość wyrzucania wyjątków z klasy Feed.</p>
<pre class="php" name="code">
&lt;?php
/*
 * Feed Exception class
 *
 * @author drymek
 *
 */
class FeedException extends Exception
{
}</pre>
<p>Klasa Debug umożliwia wypisywanie informacji kontrolnych podczas pisania kodu:</p>
<pre class="php" name="code">
&lt;?php
/*
 * Simple debug class
 *
 * @author drymek
 *
 */
class Debug
{
  /*
   * Print debug message on default output
   */
  public static function Log($message)
  {
    //Check debug mode
    if (DEBUG)
    {
      echo $message."\n";
    }
  }
}</pre>
<p>Klasa Feed! Czyli mięso właściwe. To co pobiera wszystkie informacje, przetwarza je i zapisuje:</p>
<pre class="php" name="code">
&lt;?php
require_once 'Remote.php';        //Read remote files
require_once 'FeedException.php'; //Implementation of Feed Exception
require_once 'Debug.php';         //Debug message

/*
 * Class to convert demotywatory.pl rss
 *
 * @author drymek
 *
 */
class Feed
{
  private
    $url,             //rss.xml file url
    $orginalContent,  //rss.xml file content
    $transformedDom;  //rss.xml file DOMDocument

  /*
   * Set feed url
   */
  public function setUrl($url)
  {
    $this-&gt;url = $url;
  }

  /*
   *  Read remote rss file
   */
  public function getOrginalContent()
  {
    if (is_null($this-&gt;url))
    {
      throw new FeedException("You have to set url first!");
    }

    $this-&gt;orginalContent = Remote::get_remote_file_content($this-&gt;url);
    return $this-&gt;orginalContent;
  }

  /*
   * Create a DOMDocument from rss.xml file
   */
  public function getDomOrginalContent()
  {
    if (false === $this-&gt;orginalContent || is_null($this-&gt;orginalContent))
    {
      throw new FeedException("Can not download rss file");
    }

    $domDocument = new DOMDocument();
    $domDocument-&gt;loadXML($this-&gt;orginalContent);
    return $domDocument;
  }

  /*
   * Get image URL from remote webpage
   */
  public function getImage($item)
  {
    if ('link' == $item-&gt;nodeName)
    {
      $htmlContent = Remote::get_remote_file_content($item-&gt;nodeValue);
      if (false !== $htmlContent)
      {
        //Search image tag
        $pattern = "/bigurl=\".*\"/";
        preg_match($pattern, $htmlContent, $urls);

        //Finally url to the image
        return substr($urls[0], 8, -1);
      }
      return 'http://demotywatory.pl/res/img/demotywatory-logo.gif';
    }
  }

  /*
   * Set new description for current item
   */
  public function setImage($item)
  {
    //Find element link
    foreach ($item-&gt;childNodes as $rssDomItemChild)
    {
      if ('link' == $rssDomItemChild-&gt;nodeName)
      {
        //Download demot page
        $url = $this-&gt;getImage($rssDomItemChild);
        break;
      }
    }

    //Find element description
    foreach ($item-&gt;childNodes as $rssDomItemChild)
    {
      if ('description' == $rssDomItemChild-&gt;nodeName)
      {
        $rssDomItemChild-&gt;nodeValue = '&lt;img src="'.$url.'"&gt;';
        break;
      }
    }
    Debug::Log('Found url: ['.$url.']');
  }

  /*
   * Convert rss.xml to rss.xml with images in description
   */
  public function transform()
  {
    //Get DOMDocument
    $this-&gt;transformedDom = $this-&gt;getDomOrginalContent();
    //Find all items
    $rssItems = $this-&gt;transformedDom-&gt;getElementsByTagName('item');

    //Foreach feed
    Debug::Log('Starting retriving feeds');

    foreach ($rssItems as $rssItem)
    {
      //Set image in description for node
      $this-&gt;setImage($rssItem);
    }
    Debug::Log('Finished retriving feeds!');
  }

  /*
   * Get transformed XML
   */
  public function getResults()
  {
    return $this-&gt;transformedDom-&gt;saveXml();
  }

  /*
   * Save results to file (xml)
   */
  public function save($filename)
  {
    Debug::Log('Saving file ('.$filename.')');
    $fh = fopen($filename, 'w');
    if (!$fh)
    {
      throw new FeedException('Can not open file ('.$filename.') to write!');
    }
    fwrite($fh, $this-&gt;getResults());
    fclose($fh);
  }
}</pre>
<p>I ostatni z plików który wszystko uruchamia:</p>
<pre class="php" name="code">
&lt;?php
define('DEBUG', true);    //Set debug on
require_once 'Feed.php';  //Include Feed class

//Create new Feed instance
$feed = new Feed();
//Set feed url
$feed-&gt;setUrl('http://demotywatory.pl/rss.xml');
//Get rss file content
$feed-&gt;getOrginalContent();
//Convert to new format
$feed-&gt;transform();
//Save to file
$feed-&gt;save('result.xml');</pre>
<p>Teraz wystarczy umieścić pliki na serwerze lub jeśli takiego nie posiadamy to można skorzytać np. z hostei.com (webhosting umożliwiający otwieranie plikow curl/fopen).<br />
I magia! Mamy rss z obrazkami demotywatorów w wiadomości! (Należy oczywiście odświeżać co jakiś czas nasze domotywatory, jednak użycie crona i wgeta w tej sytuacji jest w 100% wystarczające</p>
<p><a title="Demotywatorator" href="http://www.xlab.pl/wp-content/uploads/2009/10/Demotywatorator.tar.gz">Demotywatorator</a></p>
<p><a href="http://www.xlab.pl/2009/10/demotywacja-demotywatorow-ii/">&laquo; Druga część. </a></p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/demotywacja-demotywatorow/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Enterprise Architect &#8211; transformacja diagramu klas na DDL</title>
		<link>http://xlab.pl/enterprise-architect-transformacja-diagramu-klas-na-ddl/</link>
		<comments>http://xlab.pl/enterprise-architect-transformacja-diagramu-klas-na-ddl/#comments</comments>
		<pubDate>Sat, 04 Apr 2009 15:31:03 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Ogólne]]></category>
		<category><![CDATA[architect]]></category>
		<category><![CDATA[ddl]]></category>
		<category><![CDATA[diagram]]></category>
		<category><![CDATA[ea]]></category>
		<category><![CDATA[enterprise]]></category>
		<category><![CDATA[klas]]></category>
		<category><![CDATA[projektowanie]]></category>
		<category><![CDATA[uml]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=74</guid>
		<description><![CDATA[Enterprise Architect firmy Sparx Systems, to potężne narzędzie do projektowania aplikacji. Możemy w nim wykonać projekt od początku do końca. Zaczynając od wymagań klienta, możemy przejść przez wygenerowanie odpowiedniej bazy danych, kończąc na eksportowaniu dokumentacji, która po wydrukowaniu może trafić w ręce klienta. Jednak tak ogromne narzędzie ma jedną sporą wadę &#8211; trzeba mu poświęcić [...]]]></description>
			<content:encoded><![CDATA[<p>Enterprise Architect firmy <a href="http://www.sparxsystems.com.au/">Sparx Systems</a>, to potężne narzędzie do projektowania aplikacji. Możemy w nim wykonać projekt od początku do końca. Zaczynając od wymagań klienta, możemy przejść przez wygenerowanie odpowiedniej bazy danych, kończąc na eksportowaniu dokumentacji, która po wydrukowaniu może trafić w ręce klienta.</p>
<p><span id="more-74"></span></p>
<p>Jednak tak ogromne narzędzie ma jedną sporą wadę &#8211; trzeba mu poświęcić mu trochę czasu zanim stanie się ono użyteczne. W pierwszej kolejności po narysowaniu poprawnego diagramu klas chciałem wygenerować diagram DDL. Co z tego wszystkiego wynikło opisuję poniżej.</p>
<p>Opis zawiera uniwersalny sposób generacji DDL, jednak znacząco jest ukierunkowany na zgodność z symfony (zgodność nazw atrybutów, klas modelu itp.)</p>
<ol>
<li>Utworzyłem nowy projekt:<br />
Klikając kolejno:<strong> File </strong>&gt; <strong>New project</strong> wpisując nazwę pod którą będę trzymać plik projektu (wszystkie informacje zawarte są w pojedynczym pliku) kontynuowałem przyciskiem <strong>Zapisz</strong>.</li>
<li>Moim oczom ukazało się okno w którym mogę wybrać rodzaj diagramu który automatycznie chciałbym dołączyć do projektu. Zaznaczam jedynie <em>Class</em>, bo o eksporcie tego diagramy jest ten artykuł.</li>
<p style="text-align: center;">
<a href="http://www.xlab.pl/wp-content/uploads/2009/03/selectmodel.png"><img class="alignnone size-medium wp-image-76" title="Select Model" src="http://www.xlab.pl/wp-content/uploads/2009/03/selectmodel-300x265.png" alt="" width="300" height="265" /></a></p>
<li>Po zatwierdzeniu modelu w przeglądarce projektu (<em>Project Browser</em>) ukazał się pakiet (<em>package</em>) o nazwie Class Model, a w nim przykładowy model klas. Usunąłem z niego zarówno folder Systems jak i Frameworks oraz zawartość diagramu. Teraz mogę przystąpić do właściwego projektowania diagramu klas. Metodą przeciągnij i upuść z okna narzędzi (<em>Tools</em>) przeciągnąłem interesujące nas elementy diagramu. Ja w przykładzie wykorzystałem taki diagram:</li>
<p style="text-align: center;"><a href="http://www.xlab.pl/wp-content/uploads/2009/03/diagramklas.png"><img class="alignnone size-medium wp-image-77" title="Diagram Klas" src="http://www.xlab.pl/wp-content/uploads/2009/03/diagramklas-300x133.png" alt="" width="300" height="133" /></a></p>
<li>Teraz pora na transformacje na DDL. W przeglądarce projektu klikając na pakiet prawym klawiszem myszy mam dostępną opcję transformacji aktualnego pakietu (<em>Transform Current Package</em>). Zaznaczając wszystkie dostępne elementy oraz transformację DDL (określając docelowe miejsce położenia) korzystam z przycisku &#8222;Do Transform&#8221;. Po kilu sekundach został utworzony pakiet DDL wraz z jego diagramem.</li>
<p style="text-align: center;"><a href="http://www.xlab.pl/wp-content/uploads/2009/03/ddl.png"><img class="alignnone size-medium wp-image-79" title="ddl" src="http://www.xlab.pl/wp-content/uploads/2009/03/ddl-300x121.png" alt="" width="300" height="121" /></a></p>
<li>W zasadzie w tym momencie mogłbym wygenerować z diagramu plik SQL gotowy do użycia w bazie danych, gdyby nie jedna niezgodność. Zauważmy, że utworzone klucze główne oraz obce są w formacie {nazwa}ID (kategoriaID, artykulID). Symfony posiada inną konwencję ({nazwa}_id). Aby poprawnie skonfigurować nazewnictwo skorzystałem z edycji szablonów transformacji.</li>
<li style="text-align: left;">Wybierając <strong>Setting </strong>&gt; <strong>Transformation Templates</strong> otworzyłem edytor szablonów</li>
<p style="text-align: center;"><a href="http://www.xlab.pl/wp-content/uploads/2009/03/transformationeditor.png"><img class="alignnone size-medium wp-image-80" title="Transformation Editor" src="http://www.xlab.pl/wp-content/uploads/2009/03/transformationeditor-300x153.png" alt="" width="300" height="153" /></a></p>
<li>Aby skonfigurować poprawnie nazwy identifikatorów tabel oraz nazwy kluczy obcych należy wyedytowałem dwa szablony: <em>Class </em>oraz <em>Connector</em>. Odnajdując linię:
<pre class="bash">name=%qt%%CONVERT_NAME(className, "Pascal Case","Camel Case")%ID%qt%</pre>
<p>zmieniłem ją na następującą:</p>
<pre class="bash">name=%qt%%CONVERT_NAME(className, "Pascal Case","Underscored")%_id%qt%</pre>
<p>Zostaje do wprowadzenia w szablonie<em> Connector </em>dziesięć analogicznych zmian.</li>
<li style="text-align: left;">Po wykonaniu wszystkich zmian, ponownie przegenerowałem diagram DDL (jak w punkcie 4). W wyniku czego otrzymałem diagram zgodny z konwencjami symfony (wcześniej należy usunąć poprzedni diagram DDL w celu wygenerowania nowych, poprawnych nazw kluczy obcych:</li>
<p style="text-align: center;"><a href="http://www.xlab.pl/wp-content/uploads/2009/03/ddl_final.png"><img class="alignnone size-medium wp-image-83" title="ddl_final" src="http://www.xlab.pl/wp-content/uploads/2009/03/ddl_final-300x126.png" alt="" width="300" height="126" /></a></p>
<li>Nareszcie to czego oczekiwałem od samego początku! Pozostaje skorzystać z <strong>Project </strong>&gt; <strong>Database Engineering </strong>&gt; <strong>Generate Package DDL</strong>, w którym zaznaczając <em>Generate Primary/Foreign Key Constrains</em> oraz określając plik wynikowy <em>File Generation</em> &gt; <em>Single File</em> generuje za pomocą <em>Generate </em>plik sql. W tym momencie mam gotowy skrypt SQL do wczytania do bazy danych.</li>
<li>Co jednak jeśli nie chcielibyśmy tych wszystkich kroków wykonywać na każdej stacji klienckiej z Enterprise Architectem lub po prostu podzielić się ze znajomym wykonaną pracą? Nic prostrzego. Korzystając z <strong>Tools</strong> &gt; <strong>Export Reference Data </strong>możemy zaznaczyć interesujący nas <em>DDL_Transform_Template</em> i wyeksportować go do pliku xml. Taki plik możemy wczytać w dowolnym momencie do każdej stacji klienckiej korzystając z <strong>Tools </strong>&gt;<strong> Import Reference Data</strong></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/enterprise-architect-transformacja-diagramu-klas-na-ddl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automatyczna inwigilacja kodu</title>
		<link>http://xlab.pl/automatyczna-inwigilacja-kodu/</link>
		<comments>http://xlab.pl/automatyczna-inwigilacja-kodu/#comments</comments>
		<pubDate>Tue, 24 Mar 2009 19:08:33 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[codesniffer]]></category>
		<category><![CDATA[format]]></category>
		<category><![CDATA[formatter]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php_codesniffer]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=27</guid>
		<description><![CDATA[Każdy (dobry) programista chciałby, aby jego kod był najlepszy. Najważniejszą i podstawową rzeczą podczas pisania kodu, to prawidłowe nazywanie zmiennych, funkcji oraz klas. Tego jednak program nie jest w stanie sprawdzać (przynajmniej jak na razie nikt nie napisał, tak zaawansowanego analizatora kodu źródłowego). Możemy jednak zatrudnić parę skryptów aby utrzymać poprawne formatowanie kodu (tak, niektórzy [...]]]></description>
			<content:encoded><![CDATA[<p>Każdy (dobry) programista chciałby, aby jego kod był najlepszy. Najważniejszą i podstawową rzeczą podczas pisania kodu, to prawidłowe nazywanie zmiennych, funkcji oraz klas. Tego jednak program nie jest w stanie sprawdzać (przynajmniej jak na razie nikt nie napisał, tak zaawansowanego analizatora kodu źródłowego). Możemy jednak zatrudnić parę skryptów aby utrzymać poprawne formatowanie kodu (tak, niektórzy z tym także mają problemy).<br />
<span id="more-27"></span></p>
<p>Na pierwszy ogień testów poszedł <a href="http://www.waterproof.fr/products/phpCodeBeautifier/">phpCodeBeautifier</a>. Szybko jednak okazało się, że klamra otwierająca zawartość klasy musi znajdować się w tej samej linii co jej deklaracja. Istnieje opcja:</p>
<p><code><strong>--space-before-start-angle-bracket</strong></code></p>
<p>która pozwala wymusić jeden znak spacji przed &#8222;{&#8222;. Czemu autorzy nie pomyśleli o</p>
<p><code><strong>--newline-before-start-angle-bracket</strong></code></p>
<p>? Cieżko powiedzieć. Kod jest zamknięty, więc się tego nie dopisze samemu.</p>
<p>Istnieje jednak <a href="http://pear.php.net/package/PHP_CodeSniffer">PHP_CodeSniffer</a> (phpcs), który połączony z odpowiednim standardem sprawdzi składnie pliku.</p>
<p>Zainstalujmy &#8222;to&#8221; wszystko&#8230; Na początek instalacja samego phpcs:</p>
<pre name="code" class="bash">apt-get install php-codesniffer</pre>
<p>Aby aplikacja mogła sprawdzać <a href="http://trac.symfony-project.org/wiki/HowToContributeToSymfony#CodingStandards">formatowanie zgodne z symfony</a> należy jeszcze doinstalować standard symfony. Można go znaleźć w repozytorium svn pod adresem http://subversion.assembla.com/svn/phpsymfony/Symfony%20Code%20sniffer%20standards/Symfony/.</p>
<pre name="code" class="bash"> cd /usr/share/php/PHP/CodeSniffer/Standards &amp;&amp; sudo svn co http://subversion.assembla.com/svn/phpsymfony/Symfony%20Code%20sniffer%20standards/ .</pre>
<p>Do przetestowania programu stwórzymy plik:</p>
<pre name="code" class="bash">vim ~/test.php</pre>
<p>z odpowiednią zawartością:</p>
<pre name="code" class="php">&lt;?php
class TestKlasa
{
  public function testFunction()
  {
  }
}</pre>
<p>Uf, nareszcie możemy sprawdzić czy nasz kod jest poprawny:</p>
<pre name="code" class="bash">phpcs --standard=Symfony ~/test.php</pre>
<p>W wyniku czego otrzymamy:</p>
<pre name="code" class="bash">FILE: ~/test.php
--------------------------------------------------------------------------------
FOUND 3 ERROR(S) AND 0 WARNING(S) AFFECTING 2 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
2 | ERROR | Missing class doc comment
4 | ERROR | Missing function doc comment
--------------------------------------------------------------------------------</pre>
<p>Działa! To znaczy są błędy, ale o to właśnie nam chodziło &#8211; aby je wykrywać. Poprawmy więc zawartość pliku:</p>
<pre name="code" class="php">&lt;?php
/**
* Test file for PHP_CodeSniffer
*
* PHP versions 4 and 5
*
* @category Tests
* @package  XX
* @author   Marcin Dryka &lt;marcin.dryka@xsolve.pl&gt;
* @license  http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version  SVN: 1
* @filesource
*/

/**
* TestKlasa
*
* @package XX
* @author  Marcin Dryka &lt;marcin.dryka@xsolve.pl&gt;
* @version Release: 1
*/
class TestKlasa
{
  /**
  * testFunction
  *
  * @author Marcin Dryka &lt;marcin.dryka@xsolve.pl&gt;
  * @access public
  * @return void
  */
  public function testFunction()
  {
  }
}</pre>
<p>Przy tak wyglądającym pliku wynik działania phpcs jest pusty, brak błędów oznacza poprawnie sformatowany plik. Teraz potrzebna jest tylko silna wola, aby używać skryptu do sprawdzania wszystkich plików. Przy starszych aplikacjach będzie to dość kłopotliwe. Zdarzają się jednak pliki pisane od tak zwanego zera. Gdzie możemy w pełni stosować konwencje ustalone przez autorów symfony (i nie tylko).</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/automatyczna-inwigilacja-kodu/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

