<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2enclosuresfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:posterous="http://posterous.com/help/rss/1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>/dev/ulow/</title>
    <link>http://ulow.posterous.com</link>
    <description>Most recent posts at /dev/ulow/</description>
    <generator>posterous.com</generator>
    <link xmlns="http://www.w3.org/2005/Atom" href="http://posterous.com/api/sup_update#75ba8417b" type="application/json" rel="http://api.friendfeed.com/2008/03#sup" />
    
    
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/dev-ulow" /><feedburner:info uri="dev-ulow" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://posterous.superfeedr.com/" /><media:category scheme="http://www.itunes.com/dtds/podcast-1.0.dtd">Technology/Software How-To</media:category><itunes:explicit>no</itunes:explicit><itunes:subtitle>Most recent posts at /dev/ulow/</itunes:subtitle><itunes:category text="Technology"><itunes:category text="Software How-To" /></itunes:category><item>
      <pubDate>Wed, 07 Jul 2010 01:08:00 -0700</pubDate>
      <title>Autoloader</title>
      <link>http://feedproxy.google.com/~r/dev-ulow/~3/u_0HNVl7arE/autoloader</link>
      <guid isPermaLink="false">http://ulow.posterous.com/autoloader</guid>
      <description>
        <![CDATA[<p>
	
<p>Wszystkie znaki na niebie i ziemi wskazywały, że przyda się jakiś w miarę przystępny autoloader. Uratuje to moje palce jak i psychikę od pisania co chwile require_once... </p>
<p>Co ma robić? Ładować klasy automatycznie po utworzeniu instancji czy przy statycznym wywołaniu. Będzie ustawiać suffixy, foldery które będzie przeszukiwał w poszukiwaniu klas, interfejsów, czego tam chcecie i tworzył z nich mapę. Zaczynamy.</p>
<p></p>
<p>Na początek zmienne:</p>
<div class="CodeRay">
  <div class="code"><pre><code>
protected $filesSuffix = array(&quot;class&quot;);
protected $filesDir = array();
protected static $cacheFile = null;
</code></pre></div>
</div>

<p><span style="font-style: italic;">filesSuffix</span> będzie tablicą która będzie trzymała jak sama nazwa wskazuje sufiksy. Jako podstawowy damy <span style="font-style: italic;">'class'</span>, co będzie z góry szukać plików o nazwach <span style="font-style: italic;">foo.class.php</span></p>
<p><span style="font-style: italic;">filesDir</span> będzie przetrzymywać listę katalogów które będą skanowane w poszukiwaniu odpowiednich klas.</p>
<p><span style="font-style: italic;">cacheFile </span>- ścieżka do pliku z "mapą"</p>
<p>Stwórzmy te trochę mniej ważne metody, ale które będą nam pomagać i tworzyć loadera bardziej elastycznym:</p>
<p><code>


public function addSuffix($suffix) {
   if(is_string($suffix))
      $this-&gt;filesSuffix = array_push($this-&gt;filesSuffix, $suffix);
   if(is_array($suffix))
      $this-&gt;filesSuffix = array_merge($this-&gt;filesSuffix, $suffix);
}


public function addSearchDir($dir) {
   if(is_string($dir)) 
      $this-&gt;filesDir = array_push($this-&gt;filesDir, $dir);
   if(is_array($dir)) 
      $this-&gt;filesDir = array_merge($this-&gt;filesDir, $dir);
}


public static function setCacheFile($path) {
   self::$cacheFile = $path;
}


</code> </p>
<p>No i trochę objaśnień:<br /><em>addSuffix()</em> - metoda odpowiedzialna za dodawanie sufiksów do tablicy. Czyli jeżeli wywołamy ją w taki sposób<br /><code>
$foo-&gt;addSuffix("interface");
</code><br />parser będzie tworzył mapę z wszystkich plików które mają postać: <em>foo.<strong>class</strong>.php</em> i <em>foo.<strong>interface</strong>.php</em> (bo sufiks class jest już standardowo dodany). Można też jako argument dać oczywiście tablicę.<br /><code>
$foo-&gt;addSuffix(array("interface","foo","bar"));
</code></p>
<p><em>addSearchDir() - </em>tą metodą ustawiamy wszystkie katalogi, które mają być przeszukiwane. Przykład:<br /><code>
$foo-&gt;addSearchDir("/lib");
// lub tablica
$foo-&gt;addSearch(array("/lib", "/classes"));
</code></p>
<p><em>setCacheFile()</em> - ustawianie ścieżki do zserializowanego pliku z 'mapą' wszystkich klas.<br /><code>
$foo-&gt;setCacheFile("/config/classespath.txt");
</code> </p>
<p>No i przechodzimy do dwóch najważniejszych metod, odpowiedzialnych za parsowanie i ładowanie klas. Na początek parser:<br /><code>
public function parse() {
   if(file_exists(self::$cacheFile)) {
      unlink(self::$cacheFile);
   }
   foreach($this-&gt;filesDir as $dir) {
      $dirs = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
      foreach($dirs as $file) {
         $name = explode('.', $file-&gt;getFilename());
         if(in_array($name[1], $this-&gt;filesSuffix) &amp;&amp; !$file-&gt;isDir()) {
            $data = $name[0] . "|" . $file-&gt;getPathname();
            $handle = fopen(self::$cacheFile, "a");
            fwrite($handle, serialize($data));
         }
      }
   }
}
</code> <br />Na początku usuwamy plik z mapą jeżeli istnieje. Idziemy po elementach tablicy <em>$filesDir</em> i dla każdego z nich wywołuje iteratory:<br /><code>
$dirs = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
</code><br />I znów pętelka, tym razem już z niej mamy poszczególne pliki, które trzeba sprawdzić czy pasują do 'wzoru' :) Najpierw przez <em>explode</em> rozkładamy nazwę pliku na części pierwsze i sprawdzamy czy sufiks pliku znajduje się w tablicy <em>$filesSuffix </em>i czy bieżąca lokalizacja nie jest katalogiem. Do tego wykorzystujemy metodę <em>DirectoryIterator</em> zwącą się <em>isDir()<br /></em><code>
 $name = explode('.', $file-&gt;getFilename());
    if(in_array($name[1], $this-&gt;filesSuffix) &amp;&amp; !$file-&gt;isDir()) {
</code></p>
<p>Jeżeli wszystko jest tak jak chcemy, składamy dane do zapisania do kupy:<br /><code>
$data = $name[0] . "|" . $file-&gt;getPathname();
</code></p>
<p><em>$name[0]</em> jest to nazwa klasy bez sufiksu. <em>$file-&gt;getPathname()</em>, która jest znów metodą DirectoryIterator'a zwraca nam pełną ścieżkę do pliku. Potem oczywiście zapisujemy to w postaci zserializowanej:<br /><code>
            $handle = fopen(self::$cacheFile, "a");
            fwrite($handle, serialize($data));
         }
      }
   }
}
</code></p>
<p>Metoda load() jako druga najważniejsza, będzie ładować nam wszystkie klasy, interfejsy, itd.<br /><code>
public static function load($className) {
   $handle = fopen(self::$cacheFile, "r");
   $read = fread($handle, filesize(self::$cacheFile));
   $files = explode(";", $read);
   foreach($files as $file) {
      $file = unserialize($file);
      $file = explode("|", $file);
      if($className == $file[0]) {
         require_once"$file[1]";
      }
   }
}
</code></p>
<p>Na początku oczywiście otwieramy plik z mapą i wkładamy zawartość do zmiennej. Dalej 'wypakowujemy' wszystko tablicy i za pomocą pętli <em>foreach</em> 'idziemy' po kolejnych elementach. W pętli stosujemy <em>unserialize()</em> i rozkładamy string do tablicy, w której <em>$file[0]</em> to nazwa klasy a <em>$file[1]</em> to ścieżka do niej.<br /><code>
   foreach($files as $file) {
      $file = unserialize($file);
      $file = explode("|", $file)
</code><br />i sprawdzając czy nazwa klasy podanej jako argument metody jest równa nazwie klasy w kolejnej iteracji pętli, ładujemy plik:<br /><code>
      if($className == $file[0]) {
         require_once"$file[1]";
      }
   }
}
</code></p>
<p>Na końcu jeszcze tylko<br /><code>
spl_autoload_register("smAutoloader::load");
</code><p />I mamy działającą klasę Autoloadera :)<p />W takim rozwiązaniu, wszystkie pliki będą musiały mieć nazwę taką samą jak klasa wewnątrz nich, czyli plik o nazwie np: <strong>smRouter</strong>.class.php, będzie musiał zawierać klasę o nazwie <strong>smRouter</strong>. W tej wersji brakuje też wyrzucania wyjątków przy błędach czy chociażby metody, która będzie pomijać podane katalogi za pomocą wyrażenia regularnego (np katalogi SVNa), ale to już każdy z Was może dopisać samemu.</p>
<p>Teraz przykładowe wywołanie parsera, które trzeba będzie robić za każdym razem, kiedy do systemu wejdą nowe klasy, tak, aby mapa była aktualna:<br /><code>
$foo = new smAutoloader;
$foo-&gt;setCacheFile("./config/classespath.txt");
$foo-&gt;addSearchDir("./lib");
$foo-&gt;addSuffix("interface");
$foo-&gt;parse()
</code></p>
<p> </p>
	
</p>

<p><a href="http://ulow.posterous.com/autoloader">Permalink</a> 

	| <a href="http://ulow.posterous.com/autoloader#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/5Bc8iGPhRq4V</posterous:profileUrl>
        <posterous:firstName />
        <posterous:lastName />
        <posterous:nickName>ulow</posterous:nickName>
        <posterous:displayName>ulow</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://ulow.posterous.com/autoloader</feedburner:origLink></item>
  <language>en-us</language><media:rating>nonadult</media:rating></channel>
</rss>

