<?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>Creating The Web... &#187; php</title>
	<atom:link href="http://www.simon-jensen.net/tag/php/feed" rel="self" type="application/rss+xml" />
	<link>http://www.simon-jensen.net</link>
	<description>Internet entusiast og webprogrammør Simon Jensen</description>
	<lastBuildDate>Fri, 19 Aug 2011 14:22:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Håndtering af store filer i PHP</title>
		<link>http://www.simon-jensen.net/haandtering-af-store-filer-i-php.html</link>
		<comments>http://www.simon-jensen.net/haandtering-af-store-filer-i-php.html#comments</comments>
		<pubDate>Sun, 08 Mar 2009 10:38:16 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[fclose]]></category>
		<category><![CDATA[fgets]]></category>
		<category><![CDATA[fopen]]></category>
		<category><![CDATA[fseek]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/haandtering-af-store-filer-i-php.html</guid>
		<description><![CDATA[Et klassisk problem i min verden, er procesering af store filer i PHP. Det kunne f.eks. være læsning af en logfil eller importering fra en CVS fil&#8230; Jeg ved snart ikke hvormange gange jeg er begyndt at indlæse en fil uden at tænkt over filens størrelse først &#8211; det varer som regel ikke længe inden ens [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Et klassisk problem i min verden, er procesering af store filer i PHP. Det kunne f.eks. være læsning af en logfil eller importering fra en CVS fil&#8230;</strong></p>
<p>Jeg ved snart ikke hvormange gange jeg er begyndt at indlæse en fil <span style="font-style: italic;">uden</span> at tænkt over filens størrelse først &#8211; det varer som regel ikke længe inden ens computer begynder at &#8220;blæse&#8221; og et simpelt check af rammene viser at de hurtigt er ved at blive fyldt op <img src="http://www.simon-jensen.net/admin/editors/fckeditor/editor/images/smiley/msn/confused_smile.gif" alt="" />.</p>
<p>Kan du nikke genkendende til dette scenarie, så vil du sikkert kunne bruge følgende metode til noget.</p>
<h3>Step by step&#8230;</h3>
<p>Løsningen virker meget logisk, men som med alt PHP-kodning, kræver det lige at du har fundet ud af hvilke funktioner der findes til at håndterer dette. <span style="font-weight: bold;">Tricket er selvfølgelig kun at læse lidt ad gangen</span>.</p>
<p>Her har jeg taget udgangspunkt i importering af en &#8220;;&#8221;-separeret CSV-fil, og jeg er interesseret i at læse én linie ad gangen. Om filen er 10, 50, eller 100 MB stor har kun betydning for tiden det vil tage at proceserer den.</p>
<p>En linie i CVS-filen kunne se ud som følgende: ID ; navn ;  email</p>
<pre>"1234";"Simon Jensen";"min@email.xyz"</pre>
<pre><span id="more-118"></span></pre>
<h3>Læsning af CVS-filen</h3>
<p>For at åbne en fil og begynde læsningen kan vi bruge PHP-funktionen <span style="font-family: Courier New; "><a title="fopen dokumentation hos php.net" href="http://dk.php.net/fopen" target="_blank">fopen(&lt;string&gt; $file, &lt;string&gt; $mode)</a></span>. Jeg starter ud med at definerer nogle variable, som jeg vil bruge til at holde styr på hvorlangt jeg har læst i filen, samt hvormange linier jeg vil læse ad gangen.</p>
<pre>$read_chars = ($_GET[read_chars] == "") ? 0 : $_GET[read_chars]; //Current position in file
$num_of_lines = 10; //Number of lines to read

$handle = fopen("my_csv_file.cvs");
if($handle) {
    $read_lines = 0;
    while(!feof($handle) &amp;&amp; ($read_lines &lt; $num_of_lines)) {
        fseek($handle, $read_chars);
        $line = fgets($handle);
        $read_chars += strlen($line);
        $user = explode(";", $line);
        $read_lines++;
    }
}
fclose($handle);</pre>
<p>Ovenstående script er sat til at læse 10 linier per request til filen hvor scriptet kører. Hele humlen ligger i funktionen <span style="font-family: Courier New; "><a title="fseek dokumentation hos php.net" href="http://dk.php.net/fseek" target="_blank">fseek(&lt;resource&gt; $handle, &lt;int&gt; $read_chars)</a></span> samt variablen <span style="font-family: Courier New; ">$read_chars</span>. Hver gang while-løkken eksekveret sørger jeg for at hoppe direkte til positionen hvorfra jeg læste sidste, straks herefter læser jeg en enkelt linie vha. funktionen <span style="font-family: Courier New; "><a title="fgets dokumentation hos php.net" href="http://dk.php.net/fgets" target="_blank">fgets(&lt;resource&gt; $handle)</a></span>, opdaterer min variabel <span style="font-family: Courier New; ">$read_chars</span>, så vi har en nu startpositionen til næste gang.</p>
<p>Ved at ændre variablen <span style="font-family: Courier New; ">$num_of_lines</span> kan vi ændre antallet af linier scriptet skal bearbejde ad gangen. Efter scriptet har bearbejdet <span style="font-family: Courier New; ">$num_of_lines</span> antal linier, vil vi sikkert gerne bearbejde de næste <span style="font-family: Courier New; ">$num_of_lines</span> antal linier &#8211; her har jeg taget understående redirect-metode til mig.</p>
<h3>Loop til næste &#8220;runde&#8221; &#8211; header- vs. Meta-redirect</h3>
<p>Mens overstående script virker fint ved håndtering af <span style="font-family: Courier New; ">$num_of_lines</span> antal linier, skal vi have en løsning til at håndterer de næste <span style="font-family: Courier New; ">$num_of_lines</span> antal linier &#8211; og her skal vi tænke på eksekverings tiden, for vi vil jo helst gerne undgå timeouts <img src="http://www.simon-jensen.net/admin/editors/fckeditor/editor/images/smiley/msn/wink_smile.gif" alt="" />.</p>
<p>Den umiddelbare løsning kunne være at loop til selv samme fil vha. et <span style="font-family: Courier New; ">header(&#8220;Location: &#8230;&#8221;)</span> kald, men det komme vi detsvære ikke langt med. W3C-standarden har sørget for (og det er jo sådan set også fint nok), at browsere bør implementerer en advarsel hvis et script lopper til sig selv [<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3" target="_blank">w3.org</a>].</p>
<blockquote><p>A client SHOULD detect infinite redirection loops, since such loops generate network traffic for each redirection.</p></blockquote>
<p>En metode jeg er begyndt at bruge mere og mere, er et simpelt meta-redirect tag på en ganske almindelig HTML-side &#8211; meta-redirects lader til, uden problemer, at komme ud over ovenstående W3C restiktion.</p>
<pre>&lt;meta http-equiv="refresh" content="0; URL=http://redirect-url-goes-here.xyz"&gt;</pre>
<h3>Sammenfatning</h3>
<p>For at få det hele til at spille sammen, skal vi ganske simpelt redirecte vores PHP-script med <span style="font-family: Courier New; ">$read_char</span> som parameter til HTML-siden, som efterfølgende redirecter tilbage til PHP-scriptet hvor vi kan starte læsningen hvor vi slap.</p>
<p><span style="text-decoration: underline;"><span style="font-weight: bold;">script.php</span></span>:</p>
<pre><span style="font-weight: bold;">$read_chars = ($_GET[read_chars] == "") ? 0 : $_GET[read_chars];</span> //Current position in file
$num_of_lines = 10; //Number of lines to read

$handle = fopen("my_csv_file.cvs");
if($handle) {
    $read_lines = 0;
    while(!feof($handle) &amp;&amp; ($read_lines &lt; $num_of_lines)) {
        fseek($handle, $read_chars);
        $line = fgets($handle);
        $read_chars += strlen($line);
        $user = explode(";", $line);
        $read_lines++;
    }
}
fclose($handle);

<span style="font-weight: bold;">$redir = "redir.php?read_chars=".$read_chars;</span>
<span style="font-weight: bold;">header("Location: $redir");</span></pre>
<p><span style="text-decoration: underline;"><span style="font-weight: bold;">redir.php (HTML-siden)</span></span>:</p>
<pre>&lt;?php
<span style="font-weight: bold;">$read_chars = $_GET[read_chars];
$redir = "script.php?read_chars=".$read_chars;</span>
?&gt;
&lt;html&gt;
    &lt;head&gt;
        <span style="font-weight: bold;">&lt;meta http-equiv="refresh" content="0; URL=&lt;?php echo $redir; ?&gt;"&gt;</span>
    &lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Med ovenstående kan du nu håndterer dine relativt store filer uden at skulle sparke din computers ram i gulvet &#8211; der skal ikke meget fantasi til at kaste en &#8220;total antal linier&#8221;-variabel med, og med en smule procent-beregning udskrive en statusbar på HTML-siden, og så har du din helt egen &#8220;processerings maskine&#8221; med statusbar på <img src="http://www.simon-jensen.net/admin/editors/fckeditor/editor/images/smiley/msn/wink_smile.gif" alt="" />.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/haandtering-af-store-filer-i-php.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hijax i praksis</title>
		<link>http://www.simon-jensen.net/hijax-i-praksis.html</link>
		<comments>http://www.simon-jensen.net/hijax-i-praksis.html#comments</comments>
		<pubDate>Thu, 11 Sep 2008 08:06:54 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[hijax]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/hijax-i-praksis.html</guid>
		<description><![CDATA[Tilbage i april fandt jeg frem til hijax princippet, og i den forbindelse lovede jeg at der ville komme en mere praktisk gennemgang &#8211; det vil jeg så forsøge mig med nu. Mens sidste post om hijax kort fortalte princippet bag hijax, og snakken mere gik på &#8220;hvorfor&#8221; man burde bruge det, har jeg nu [...]]]></description>
			<content:encoded><![CDATA[<p><span style="font-weight: bold;">Tilbage i april fandt jeg frem til </span><a title="Hijax" href="http://www.simon-jensen.net/hijax.html"><span style="font-weight: bold;">hijax</span></a><span style="font-weight: bold;"> princippet, og i den forbindelse lovede jeg at der ville komme en mere praktisk gennemgang &#8211; det vil jeg så forsøge mig med nu.</span></p>
<p>Mens sidste post om hijax kort fortalte princippet bag hijax, og snakken mere gik på &#8220;hvorfor&#8221; man burde bruge det, har jeg nu haft mulighed for rent faktisk at lege lidt med hijax, og mener nu der skulle være basis for at skrive endnu en post herom.</p>
<h3>Hijax super kort</h3>
<p>Jeg har taget udgangspunkt i et simpelt eksempel, som jeg selv synes forklare det praktiske bag princippet. Super kort fortalt, så går hijax ud på at &#8220;hijacke&#8221; (overtage) funktionalitet på din hjemmeside og udføre denne via AJAX, men <span style="font-style: italic;">SAMTIDIG</span> skal det være muligt at afvikle samme funktionalitet <span style="font-style: italic;">UDEN</span> brug af Javascript &#8211; grundtanken i <a title="Læs mere om denne ide på Wikipedia" href="http://en.wikipedia.org/wiki/Fault-tolerant_system" target="_blank">graceful degradation</a>.</p>
<p><span id="more-110"></span></p>
<h3>Hijax en formular</h3>
<p>Hijax kan f.eks. bruges i forbindelse med en formular &#8211; I denne post har jeg taget udgangspunkt i en simpelt udgave af en tilmeldingsformular til et tænkt nyhedsbrev, men i princippet kan hijax bruges til en hvilken som helst formular.</p>
<p><span style="font-weight: bold;">HTML formularen:</span></p>
<p>Formularen er meget standard med to inputs og postering via POST-metoden. Vi kan bruge <span style="font-family: Courier New;">onsubmit</span>-attributten til at hijacke formularen. <span style="font-family: Courier New;">onsubmit</span> giver os nemlig mulighed for at udføre en stump Javascript <em>før</em> dataen vil blive postet til serveren. I eksemplet herunder tager jeg hele formularen med som parameter til Javascript funktionen <span style="font-family: Courier New;">hijax(form)</span>. For at være sikker på at det &#8220;almindelige&#8221; POST ikke bliver udført returnerer vi <span style="font-family: Courier New;">false</span> efterfølgende&#8230; da dette faktisk er direkte Javascripting i formularen vil dennes postering <em>kun</em> stoppes hvis Javascript er understøttet.</p>
<pre>&lt;form method="post" action="<strong>signup.php</strong>" onsubmit="<strong>hijax(this); return false;</strong>"&gt;
    &lt;label&gt;Name:&lt;/label&gt;
    &lt;input type="text" name="name"   /&gt;
    &lt;label&gt;E-mail:&lt;/label&gt;
    &lt;input type="text" name="email"   /&gt;
    &lt;input type="submit"   /&gt;
&lt;/form&gt;</pre>
<p><strong>Javascriptet:</strong></p>
<p>Selve Javascriptet behøver ikke være mere indviklet end vist herunder. Jeg har endnu engang taget udgangspunkt i et AJAX request med <a title="Prototype JS" href="http://www.prototypejs.org/" target="_blank">Prototype JS</a>.</p>
<p>Her serializerer jeg hele formularen, og tilføjet en ekstra parameter <span style="font-family: Courier New;"><strong>hijax=true</strong></span>. Selve værdien er denne parameter bruger jeg faktisk ikke til noget, hvilket du vil kunne se i understående PHP-script. Vi poster det hele til PHP-filen <span style="font-family: Courier New;">signup.php</span>, hvilken også var action-fil i HTML-formularen ovenfor.</p>
<pre>hijax = function(formular) {
    var url = <strong>signup.php</strong>;
    var params = Form.serialize(formular) + <strong>&amp;hijax=true</strong>;
    new Ajax.Request(url, {method:post, postBody:params});
}</pre>
<p><strong>PHP serveren:</strong></p>
<p>Som programmør forsøger jeg at være doven!? Jeg vil gerne have et optimalt resultat af min kode, og jeg vil skrive så få linier som muligt. Og her er hijax fantastisk, for ligemeget om vi posterer formularen &#8220;almindeligt&#8221; eller via AJAX, kan vi bruge samme fil til at håndtere posteringen.</p>
<p>Da jeg også forsøger at være lidt af en sucker for brugervenlighed, vil jeg gerne have mulighed for at give brugeren et response ligemeget om denne har Javascript-understøttelse eller ej. Tricket er altså at kunne adskille de to posteringer fra hinanden. Jeg har brugt den ekstra parameter ved AJAX-posteringer, men du vil sikkert selv kunne komme på andre løsninger (<em>tilføj dem gerne i kommentaren</em>)?</p>
<pre>&lt;?php
session_start();
$name = mysql_real_escape_string($_POST[name]);
$email = mysql_real_escape_string($_POST[email]);
<strong>$isHijax = $_POST[hijax];</strong>
mysql_query("INSERT INTO signups (name, email) VALUES ($name, $email)");
if(isset(<strong>$isHijax)</strong>) {
    <strong>echo</strong> Saved using AJAX...;
} else {
    <strong>$_SESSION[message]</strong> = Saved using normal post...;
    $redirect = $_SERVER[HTTP_REFERER];
    header("Location: $redirect");
}
?&gt;</pre>
<p>Med ovenstående metode vil du kunne &#8220;hijaxe&#8221; en formular, og med lidt få ændringer i formularens action-fil, vil du også have mulighed for at give brugerene et response, ligemeget om de bruger AJAX eller ej &#8211; Det er skudda smart <img src="/admin/editors/fckeditor/editor/images/smiley/msn/wink_smile.gif" alt="" />.</p>
<h3>Download</h3>
<p align="center"><a href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=5"><img title="Download" src="http://www.simon-jensen.net/admin/uploads/image/download.png" alt="Download" width="119" height="104" align="absMiddle" /></a> <a title="Download kode-eksempel på hijax" href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=5">Hijax af en formular (.zip)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/hijax-i-praksis.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP5 Overloading af constructors</title>
		<link>http://www.simon-jensen.net/php5-overloading-af-constructors.html</link>
		<comments>http://www.simon-jensen.net/php5-overloading-af-constructors.html#comments</comments>
		<pubDate>Sun, 10 Aug 2008 14:24:54 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/php5-overloading-af-constructors.html</guid>
		<description><![CDATA[Jeg sidder i skrivende stund og fors&#248;ger at forbedre mine evner med OOP i PHP5. Og har man f&#248;rste sagt OOP, kommer man vel ikke uden om at sige constructor og overloading af samme &#8230; eller hvad?! Da PHP5 udkom, var der en del hype om forbedringer i henhold til at kode objekt orienteret heri, [...]]]></description>
			<content:encoded><![CDATA[<p><span class="Apple-style-span" style="font-weight: bold;">Jeg sidder i skrivende stund og fors&oslash;ger at forbedre mine evner med OOP i PHP5. Og har man f&oslash;rste sagt OOP, kommer man vel ikke uden om at sige constructor og overloading af samme &#8230; eller hvad?!</span></p>
<p>Da PHP5 udkom, var der en del hype om forbedringer i henhold til at kode objekt orienteret heri, der er bl.a. introduceret en ny m&aring;de at lave constructors p&aring; samt mulighed for at bestemme public/private/protected identifyers til funktioner.</p>
<p>Hvor man i PHP4 blot skulle defineret constructors som en funktion med samme navn som klassen, kan man nu bruge <span style="font-family: Courier New; ">__construct()</span> og evt. <span style="font-family: Courier New; ">__destruct()</span> &#8211; men hvad med overloading af constructors?</p>
<p>I sprog som f.eks. C# og Java overloader du constructors blot ved at tilf&oslash;je endnu en funktion med klassens navn, og med et unikt antal parametre:</p>
<pre>
class Test {
&nbsp;&nbsp;&nbsp;&nbsp;public Test() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Default constructor
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;public Test(int foo) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Constructor using 1 parameter
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;public Test(int foo, int bar) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Constructor using 2 parameters
&nbsp;&nbsp;&nbsp;&nbsp;}
}</pre>
<p>Det skulle v&aelig;re forholdsvis let at komme til at tro, at man kunne udf&oslash;re noget lignende i PHP5, nu det jo er kommet med p&aring; noderne&nbsp;<img alt="" src="/admin/editors/fckeditor/editor/images/smiley/msn/tounge_smile.gif">&nbsp;- men s&aring;dan forholder det sig ikke helt. Jeg fors&oslash;gte f.eks. til at starte med, at lave en overloading ved noget ligende:</p>
<pre>
public function __construct($foo) {
&nbsp;&nbsp;&nbsp;&nbsp;//Constructor using 1 parameter
}</pre>
<p>Men da jeg samtidig havde defineret en standard constructor, fik jeg en fejlmelding:</p>
<pre>
Fatal error: Cannot redeclare Test::__construct()
</pre>
<p>Underligt t&aelig;nkte jeg, og begyndte at <a target="_blank" title="Google s&oslash;gning p&aring;: PHP5 constructor overloading" href="http://www.google.com/search?q=php5+constructor+overloading">Google overloading af constructors</a> lidt. Og det lader &aring;benbart til at v&aelig;re et generelt problem/irritationsmoment.</p>
<h3>Constructor overloading work-around</h3>
<p>Jeg fandt dog frem til f&oslash;lgende work-around, hvilket virker udem&aelig;rket &#8211; men &aelig;rlig talt, hvorfor har man dog lader et s&aring; basal ting udeblive i et sprog nu man alligevel var igang med at forbedre dets muligheder for OOP?!</p>
<pre>
public function __construct() {
&nbsp;&nbsp;&nbsp;&nbsp;$num = func_num_args();
&nbsp;&nbsp;&nbsp;&nbsp;$args = func_get_args();
&nbsp;&nbsp;&nbsp;&nbsp;switch($num) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case 1:
	&nbsp;&nbsp;&nbsp;&nbsp;$this->__call(__construct1, $args);
	&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case 2:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$this->__call(__construct2, $args);
	default:
	&nbsp;&nbsp;&nbsp;&nbsp;//Do default stuff
	}
}</pre>
<p>Ved at &quot;switche&quot; p&aring; antallet af input parametre til constructoren (ogs&aring; selvom man ikke skal definere dem i selve constructoren &#8211; WTF?), og benytte en interne function <span style="font-family: Courier New; ">__call</span>, som meget simpelt kalder en anden funktion med de sendte input paramtre, kan man f&aring; sin kode til at &quot;se ud til&quot; man overloader constructoren.</p>
<p>I virkeligheden kalder vi bare nogle andre funktioner, her meget s&oslash;gt kaldet <span style="font-family: Courier New; ">__construct1/2/3</span>, alt efter antallet af parametre.</p>
<pre>
public function __construct1($param) {
&nbsp;&nbsp;&nbsp;&nbsp;//do stuff with your one parameter
}

public function __construct2($param1, $param2) {
&nbsp;&nbsp;&nbsp;&nbsp;//do stuff with 2 parameters!
}</pre>
<p>Funktionen til at kalde vores &quot;overloadede constructors&quot; ser ud s&aring;ledes:</p>
<pre>
private function __call($name, $arg) {
&nbsp;&nbsp;&nbsp;&nbsp;return call_user_func_array(array($this, $name), $arg);
}</pre>
<p>L&oslash;sningen fungere udem&aelig;rket, om ikke andet er det da rimelig transparent for den der efterf&oslash;lgende skal bruger klassen&#8230; Ved ikke helt hvad jeg synes om det, men indtil andet er p&aring; plads (i PHP), s&aring; vil jeg tro dette er m&aring;den at g&oslash;re det p&aring;.</p>
<p>Er der nogen der er kommet frem til andre m&aring;der at overloade constructors p&aring; i PHP?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/php5-overloading-af-constructors.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Jeg har fået Mac!</title>
		<link>http://www.simon-jensen.net/jeg-har-faaet-mac.html</link>
		<comments>http://www.simon-jensen.net/jeg-har-faaet-mac.html#comments</comments>
		<pubDate>Sat, 02 Aug 2008 22:46:45 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Alt Andet]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[leopard]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[pc]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[vista]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/jeg-har-faaet-mac.html</guid>
		<description><![CDATA[Lige siden min kæreste, for nu snart 3 år siden, købte sig en iBook, har jeg haft et misundeligt blik i øjnene hver gang den blev hevet frem. Lad mig bare sige det med det samme &#8230; jeg har svaghed for eye-candy, funky features og design Nu kom dagen så, hvor jeg stod i en [...]]]></description>
			<content:encoded><![CDATA[<p align="center"><object classid="clsid:02bf25d5-8c17-4b23-bc80-d3488abddc6b" width="480" height="376" codebase="http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"><param name="autoplay" value="false" /><param name="src" value="http://movies.apple.com/movies/us/apple/getamac_ads2/box_480x376.mov" /><embed type="video/quicktime" width="480" height="376" src="http://movies.apple.com/movies/us/apple/getamac_ads2/box_480x376.mov" autoplay="false"></embed></object></p>
<p>Lige siden min kæreste, for nu snart 3 år siden, købte sig en iBook, har jeg haft et misundeligt blik i øjnene hver gang den blev hevet frem. Lad mig bare sige det med det samme &#8230; jeg har svaghed for eye-candy, funky features og design <img src='http://www.simon-jensen.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Nu kom dagen så, hvor jeg stod i en situation, hvor jeg (igen) skulle ud og erhverve mig en ny computer &#8211; og denne gang tog jeg springt til Mac!</p>
<p>Det er ikke fordi jeg, ligesom visse <a title="Trial software på PC!" rel="nofollow" href="http://www.demib.dk/ny-computer-spam-826.html" target="_blank">andre</a>, nødvendigvis synes PC og Windows har været noget være <a title="Demib siger sin mening om Windows Vista." rel="nofollow" href="http://www.demib.dk/windows-vista-sucks-828.html" target="_blank">lort</a>! Faktisk har jeg været riigtig glad for den sidste PC jeg havde (<a title="Dell XPS M1330" href="http://www.dell.com/content/products/productdetails.aspx/xpsnb_m1330?c=us&amp;l=en&amp;s=dhs&amp;cs=19" target="_blank">Dell XPS M1330</a>), og har ikke oplevet nogle bemærkelsesværdige problemer med Windows Vista &#8211; nu var jeg bare klar til at prøve en Mac.</p>
<p><span id="more-114"></span></p>
<h3>Out of the box &#8230; and ready!</h3>
<p>Når PC nu har fået et (sidste) klap på skulderen, må jeg jo nu nok også hellere gøre det samme med Mac!</p>
<p>Jeg valgte ovenstående video (ud af <a title="Reklamefilm fra Apple." href="http://www.apple.com/getamac/ads/" target="_blank">maaaaaange rigtig sjove)</a> fra Apple, fordi noget af det første der slog mig var hvor hurtigt man faktisk var igang med at bruge maskinen! Nu ved jeg godt, at jeg nok selv ender med at komme til at lyde som en af de der &#8220;frelste nørder&#8221; som har fundet lykken i en Mac (har selv et par af de dem), men jeg kan ikke komme uden om det &#8211; en Mac kommer med det man skal bruge, og ikke andet!</p>
<p>Jeg er webprogrammør med en hæftig træng til PHP (hvilket jo er platformsuafhængig), så det skulle jo ikke blive noget problem at kode på en Mac! Det er det absolut heller ikke &#8211; men jeg havde ikke regnet med at det skulle være SÅ let at komme igang! Mac´s OS Leopard kommer nemlig præ-installeret med en Apache webserver samt PHP &#8211; alt alt jeg skulle gøre var at trykke på en knap for at aktivere det &#8230; næsten da.</p>
<h3>Opsætning af PHP på Mac</h3>
<p>Apache servere er helt standard, og kan startes og stoppes ved et tryk på en knap &#8230; for at aktivere PHP på Mac, skal der fjernes udkommenteringen af én linier i Apaches konfigurations fil.</p>
<p>Åben en konsol og åben konfigurationsfilen f.eks. med &#8220;pico&#8221;:</p>
<pre>pico /etc/apache2/httpd.confg</pre>
<p>Find følgende linie, og fjern #-tegnet foran linien, så den står som herunder:</p>
<pre>LoadModule php5_module libexec/apache2/libphp5.so</pre>
<p>Genstart, eller bare start, &#8220;Websharing&#8221; fra &#8220;System Preferences =&gt; Sharing&#8221; og du skulle være klar til at bruge PHP på Mac!</p>
<p>&#8230;</p>
<p>Og nååhhh ja &#8211; så er der kommet nyt design på bloggen (igen) &#8211; se hvordan det har set ud før på min <a title="Webdesigns på www.simon-jensen.net" href="http://flickr.com/photos/simon_jensen/tags/website/" target="_blank">Flickr</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/jeg-har-faaet-mac.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://movies.apple.com/movies/us/apple/getamac_ads2/box_480x376.mov" length="2534807" type="video/quicktime" />
		</item>
		<item>
		<title>AJAX request til andet domæne</title>
		<link>http://www.simon-jensen.net/ajax-request-til-andet-domaene.html</link>
		<comments>http://www.simon-jensen.net/ajax-request-til-andet-domaene.html#comments</comments>
		<pubDate>Wed, 04 Jun 2008 15:40:32 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[pear]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[proxy]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/ajax-request-til-andet-domaene.html</guid>
		<description><![CDATA[Har du leget med AJAX har du muligvis også fundet ud af, at det ikke bare kan lade sig gøre at lave et request til et andet domæne? Rent faktisk, kan det ikke engang (bare sådan lige) lad sig gøre, at lave et request til en anden server på samme domæne! Og dog &#8230; Grunden [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Har du leget med AJAX har du muligvis også fundet ud af, at det ikke bare kan lade sig gøre at lave et <em>request</em> til et andet domæne? Rent faktisk, kan det ikke engang (bare sådan lige) lad sig gøre, at lave et request til en anden server på samme domæne! Og dog &#8230;</strong></p>
<p>Grunden til at du måske har siddet og mumlet lidt i skægget, skal findes hos webbrowseren, det er nemlig her restriktionen ligger. Jeg tror ikke jeg kan forklare det meget bedre end <a title="Beskrivelse af Cross-domain AJAX hos Yahoo!" href="http://developer.yahoo.com/javascript/howto-proxy.html" target="_blank">Yahoo!</a>, som skriver følgende:</p>
<blockquote><p>All modern web browsers impose a security restriction on network connections, which includes calls to XMLHttpRequest. This restriction prevents a script or application from making a connection to any web server other than the one the web page originally came from.</p></blockquote>
<p>But fear not &#8211; der selvfølgelig en løsning &#8230;</p>
<p><span id="more-86"></span></p>
<h3>No-go scenariet</h3>
<p>Lad os starte med at forklare <em>no-go</em> scenariet visuelt (tak til Yahoo! for allerede at have lavet grafiken <img src="http://www.simon-jensen.net/admin/editors/fckeditor/editor/images/smiley/msn/tounge_smile.gif" alt="" width="19" height="19" />).</p>
<p>Har du en webapplikation, som findes på en webserver, og afvikles i en browser hos den besøgende, kan det altså ikke lade sig gøre, at lave et request til en ekstern server/service som f.eks. Yahoo! via XMLHttpRequest (AJAX) &#8211; Dette sørger browseren for, vha. den omtalte restiktion ved denne type kald.</p>
<p>Et sådanne forsøg, vil se ud som herunder:</p>
<p><img src="http://www.simon-jensen.net/admin/uploads/image/cross-domain-ajax/proxy2.gif" border="1" alt="No-go scenarie for et cross domain AJAX request." width="347" height="284" /></p>
<h3>Do-go scenariet</h3>
<p>Vi ved altså at restiktionen ligger hos browseren &#8211; Samtidig kan jeg så fortælle, at det <em>godt kan lade sig gøre</em> at lave et request fra domænets server til en anden ekstern server/service/domæne. Når det er sagt, så kan du vel næsten gætte dig til, hvordan vi kan overkomme at lave et AJAX-request til et andet domæne?!</p>
<p>Jeps &#8211; I stedet for at lave AJAX-requestet direkte til den eksterne server, kalder vi en <em>proxy</em>, på vores egen server. Med proxy, menes faktisk bare et script som sender kaldet videre til den eksterne server, samt leverer responset herfra tilbage til browseren.</p>
<p>Scenariet ser ud som herunder:</p>
<p><img src="http://www.simon-jensen.net/admin/uploads/image/cross-domain-ajax/proxy3.gif" border="1" alt="Do-go scenarie for et cross domain AJAX request." width="525" height="201" /></p>
<p>En sådanne proxy kan laves på flere måder. Hos Yahoo! kan du <a title="Simpel Web Proxy til cross domain requests fra Yahoo!" href="http://developer.yahoo.com/javascript/samples/proxy/php_proxy_simple.txt" target="_blank">downloade en version som benytter CURL</a>. Jeg har leget med en version som benytter sig af <a title="PEAR :: The PHP Extension and Application Repository." href="http://pear.php.net/" target="_blank">PEAR</a>.</p>
<h3>Proxy med PEAR HTTP/Request</h3>
<p>PEAR er et stort framework som fungerer som et pakke-system. PEAR leverer pakker/klasser til mere eller mindre alt, hvad du kunne tænke dig &#8211; men for at lave vores omtalte proxy, skal vi faktisk ikke bruge andet, end en standard PEAR-error-klasse samt en Request-klasse til en HTTP pakke &#8211; det lyder indviklet?! Bare rolig, du kan <a href="#download">downloade det hele </a>i slutningen af denne post <img src="http://www.simon-jensen.net/admin/editors/fckeditor/editor/images/smiley/msn/regular_smile.gif" alt="" width="19" height="19" />.</p>
<p><strong>Server proxy</strong></p>
<p>Et lidt simplificeret eksempel på en proxy med PEAR, ser ud som herunder:</p>
<pre>&lt;?php
require_once "HTTP/Request.php";

$url = $_REQUEST[url];
$req =&amp; new HTTP_Request($url);

if (!PEAR::isError($req-&gt;sendRequest())) {
    print $req-&gt;getResponseBody();
}
?&gt;</pre>
<p><strong>Klienten / Javascriptet</strong></p>
<p>Proxy-scriptet skal altså modtage den URL som den skal hente. Der oprettes et objekt af typen HTTP_Request, som vi kan bruge til at sende requestet videre med, samt &#8220;print&#8221; responset. Responset skal herefter modtages på klient-siden, og endelig udskrives &#8211; dette kunne se ud som herunder:</p>
<pre>proxy_request = function()
{
    $("loading").toggle();
    var info = function(t) {
        $("loading").toggle();
        $("status").innerHTML = t.responseText;
    };

    var url = proxy.php?reg_url=   encodeURIComponent("http://flickr.com/photos/simon_jensen/");
    new Ajax.Request(url, {method:get, onSuccess:info, onFailure:info});
}</pre>
<h3><a name="download"></a>Download</h3>
<p align="center"><a href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=4"><img src="http://www.simon-jensen.net/admin/uploads/image/download.png" alt="Download" width="119" height="104" align="absMiddle" /></a> <a title="Download" href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=4">Cross Domain AJAX Request (.zip)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/ajax-request-til-andet-domaene.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dynamiske URLer uden duplicate content</title>
		<link>http://www.simon-jensen.net/dynamiske-urler-uden-duplicate-content.html</link>
		<comments>http://www.simon-jensen.net/dynamiske-urler-uden-duplicate-content.html#comments</comments>
		<pubDate>Sun, 13 Apr 2008 11:23:17 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[mod_rewrite]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[seo]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/dynamiske-urler-uden-duplicate-content.html</guid>
		<description><![CDATA[Som min gode ven Marcel Fuursted skrev om, for noget tid siden, findes der på mange dynamisk genererede sider, ofte problemer med duplicate content. Problemet findes i flere CMSer, webshops og ganske sikkert en del andre systemer, som automatisk genererer en URL til en &#8220;unik&#8221; side. Jeg vil ikke gå meget i dybden med begrebet [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Som min gode ven Marcel Fuursted skrev om, for noget tid siden, findes der på mange dynamisk genererede sider, ofte problemer med </strong><a title="Duplicate content og dynamisk søgemaskineoptimering." href="http://www.fuursted.net/dubplicate-dynamisk-seo.aspx" target="_blank"><strong>duplicate content</strong></a><strong>. Problemet findes i flere CMSer, webshops og ganske sikkert en del andre systemer, som automatisk genererer en URL til en &#8220;unik&#8221; side.</strong><em> </em></p>
<p>Jeg vil ikke gå meget i dybden med begrebet duplicate content &#8211; det er vidst <a title="Marcel Fuursted" href="http://www.fuursted.net/hvad-er-dublicate-content.aspx" target="_blank">beskrevet</a> <a title="Google Webmaster Help" href="http://www.google.com/support/webmasters/bin/answer.py?hl=en&amp;answer=66359" target="_blank">op til</a> <a title="Matt Cutts" href="http://www.mattcutts.com/blog/duplicate-content-question/" target="_blank">flere gange</a> &#8211; jeg vil lige forklare lidt om hvordan det kan opstå, for herefter at give et foreslag til hvor let det egenligt er, at komme ud over!</p>
<h3>Sådan kan duplicate content opstå</h3>
<p>Mange dynamiske sider henter deres indhold fra en database ved direkte, at lave en query med f.eks. et ID. Forestil dig URLen:</p>
<p><span style="font-family: Courier New; ">/index.php?pageID=123</span></p>
<p><span style="font-family: Courier New; ">index.php</span> kunne hente og udskrive indholdet fra databasen, hvor id er 123, ved følgende simple query:</p>
<pre>$ID = mysql_real_escape_string(<strong>$_GET["pageID"]</strong>); //secure string for DB
$query = mysql_query("SELECT content FROM content WHERE <strong>id</strong>=".$ID."");
$dbObj = mysql_fetch_object($query);
echo $dbObj-&gt;content;</pre>
<p><span id="more-107"></span></p>
<p>Ovenstående <em>vil</em> ofte virke som man regner med, men &#8220;<span style="font-family: Courier New; ">/index.php?pageID=123</span>&#8221; er langt fra den eneste URL der giver det forventede resultat. Hvad med følgende:</p>
<p><span style="font-family: Courier New; ">/index.php?pageID=123&amp;somevar=asdf</span></p>
<p><span style="font-family: Courier New; ">/index.php?pageID=123&amp;somevar=asdf&amp;someothervar=blabla</span></p>
<p><span style="font-family: Courier New; ">/index.php?pageID=123&amp;yetanother=irrelevant</span></p>
<p>Da alle ovenstående URLer har sat variablen <span style="font-family: Courier New; ">pageID</span> til 123, hvilket er alt hvad vores database query skal bruge, vil samtlige URLer give samme resultat! De efterfølgende variabler (<span style="font-family: Courier New; ">somevar</span>, <span style="font-family: Courier New; ">someother</span>, <span style="font-family: Courier New; ">yetanother</span>) er altså irrelevante, men linket <em>kan</em> stadig figurerer og virker altså lige så godt som den oprindelige!</p>
<h3>Sådan kan duplicate content undgåes</h3>
<p>Jeg har tidligere haft skrevet om <a title="Læs den tidligere post om &quot;mod_rewrite&quot;." href="http://www.simon-jensen.net/mod-rewrite.html">mod_rewrite</a> modulet til Apache, hvilket lader dig omskrive en URL, for herefter at parse denne til specifik fil.</p>
<p>Idag laver jeg ikke en PHP-løsning uden at bruge mod_rewrite, og løsningen ligger så meget op til, at man skal verificerer den indkommende URL, at duplicate content nemt bliver elimineret i processen. Det skal dog nævnes, at denne løsning også sagtens kan lade sig gøre, hvis du benytter URLer som i eksemplet ovenfor!</p>
<p>Jeg har til hver af mine poster et felt i databasen som indeholder den fulde URL (uden domænenavn) til hver post. I tilfældet af denne post, er værdien som du nok har gættet:</p>
<p><span style="font-family: Courier New; ">/dynamiske-urler-uden-duplicate-content.html</span></p>
<p>Når du går ind på denne URL, skal jeg for at hente indholdet, lave en query til min databasen. Da jeg ikke har en <span style="font-family: Courier New; ">pageID</span> variabel eller lignende, benytter jeg den fulde URL som identifyer (læs: id). Queryen kunne se ud, nogenlunde, som følgende:</p>
<pre>$url = mysql_real_escape_string(<strong>$_SERVER[REQUEST_URI]</strong>); //secure string for DB
$query = mysql_query("SELECT content FROM content WHERE <strong>url</strong>=".$url."");
$dbObj = mysql_fetch_object($query);
echo $dbObj-&gt;content;</pre>
<p>Prøver du at tilføje ekstra variabler til denne post, vil du se, at du får en 404 fejl. Dette gøres ganske simpelt, ved lige at checke om queryen også gav noget resultat, og hvis ikke, kaste en header med en fejlkode inden der forsøges at udskrive noget:</p>
<pre>//query goes here...
...
if(empty($dbObj)) {
    header("HTTP/1.1 404 Not Found");
    header("Status: 404 Not Found");
    exit();
} else {
    echo $dbObj-&gt;content;
}</pre>
<p>For at den besøgende ikke bare møder en kedelig standard 404-side, kan du designe en speciel side, som du vil bruge til dette formål &#8211; tilføj følgende linie i din  <span style="font-family: Courier New; ">.htaccess</span>, for at henvise 404-fejl til en speciel side:</p>
<pre>ErrorDocument 404 /404.php</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/dynamiske-urler-uden-duplicate-content.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Live Search med Prototype JS og PHP</title>
		<link>http://www.simon-jensen.net/live-search-med-prototype-js-og-php.html</link>
		<comments>http://www.simon-jensen.net/live-search-med-prototype-js-og-php.html#comments</comments>
		<pubDate>Sun, 09 Mar 2008 11:03:41 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[live search]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[prototype]]></category>
		<category><![CDATA[script.aculo.us]]></category>
		<category><![CDATA[xhtml]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/live-search-med-prototype-js-og-php.html</guid>
		<description><![CDATA[Update 28/04-08: Live-search understøtter nu ÆØÅ &#8211; download ny version i slutningen af denne post! I forbindelse med det sidste nye redesign, har jeg oprettet en &#8220;Live search&#8221;. Nåhhh ja, den er måske ikke så live som en definition vil have det &#8211; men det er da en fed feature &#8211; og funktionaliteten bag den, vil [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.simon-jensen.net/admin/uploads/image/information.png" alt="Update" width="16" height="16" /> <span style="text-decoration: underline;"><strong>Update 28/04-08: Live-search understøtter nu ÆØÅ &#8211; </strong></span><a title="Download Live-Search" href="http://www.simon-jensen.net/live-search-med-prototype-js-og-php.html#download"><span style="text-decoration: underline;"><strong>download ny version</strong></span></a><span style="text-decoration: underline;"><strong> i slutningen af denne post!</strong></span></p>
<p><strong>I forbindelse med det sidste nye redesign, har jeg oprettet en &#8220;Live search&#8221;. Nåhhh ja, den er måske ikke så <em>live</em> som en definition vil have det &#8211; men det er da en fed feature &#8211; og funktionaliteten bag den, vil jeg da gerne dele med jer.</strong></p>
<p><a title="Live Search" href="http://www.simon-jensen.net/admin/uploads/image/live-search/live-search.jpg"><img style="width: 184px; height: 86px;" title="Live Search" src="http://www.simon-jensen.net/admin/uploads/image/live-search/live-search.jpg" border="1" alt="Live Search" align="right" /></a>Denne post består således af en gennemgang, af de vigtigste punkter i implementeringen, det være sig lidt XHTML, CSS, Javascript samt PHP.</p>
<p>I slutningen af posten, kan du desuden <a title="Gå direkte til download af &quot;Live search&quot;." href="http://www.simon-jensen.net/live-search-med-prototype-js-og-php.html#download">downloade</a> en lille demo-version af funktionen.</p>
<p>For at benytte koden i denne post kræves det, at du har downloaded og inkluderet Javascript frameworket <a title="Læs mere og download Prototype her." href="http://www.prototypejs.org/" target="_blank">Prototype JS</a>, samt effektbiblioteket <a title="Læs mere og download script.aculo.us her." href="http://script.aculo.us" target="_blank">script.aculo.us</a>. Disse skal inkluderes i sidens <span style="font-family: Courier New;">&lt;head&gt;</span>-sektion, som med et hvert andet Javascript.</p>
<p><span id="more-91"></span></p>
<h3>XHTML</h3>
<p>Jeg har gjort det til god skik, at oprette en &#8220;container&#8221;-div. Dette div er ikke nødvendig, men her er det taget med for at få bare en smule design med.</p>
<p>Inde i denne container findes et billede, som er stylet til at være skjuldt. Dette er hvad jeg bruger som loading animation &#8211; altså en animation der skal vise, at et script arbejder. Input feltet skulle gerne tale for sig selv. Straks herunder er der, i endnu et div, oprettet en ul/li-liste &#8211; det er denne vi vil fremvise resultater i. Resultaterne skal ligeledes fremkommet/tilføjes som li-elementer til listen.</p>
<pre>&lt;div id="container"&gt;
    &lt;img src="loading.gif" id="lsloading" align="right" alt="" style="display:none;"  /&gt;
    &lt;input type="text" value="Type and you´ll find..." id="lsquery"  /&gt;
    &lt;br  /&gt;
    &lt;div id="lsresults" style="display:none;"&gt;
        &lt;ul id="lsResList"&gt;
            &lt;li&gt;&lt;i&gt;Type and you´ll find...&lt;/i&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre>
<h3>CSS</h3>
<p>CSS delen af dette script, taler også lidt for sig selv. Det er muligt, at designer kan laves en hel del nemmere (læs: med mind css), men her er indsat hvad du finder i demoen.</p>
<p>Det vigtigste her, vil nok være at påpege, at resultatlisten er sat til absolut positionering. Dvs. vi kan vise den ovenpå andre elementer, hvorfor den ikke skal til at &#8220;skubbe&#8221; rundt på designet. Du har måske bemærket at resultatlisten er en smule gennemsigtigt &#8211; Dette ER en CSS feature, men den bliver udført gennem Javascript-delen.</p>
<pre>body {
    font-family: "Trebuchet MS";
    font-size: 1em;
    background: #333;
}

#container {
    background: #fff;
    width: 400px;
    padding: 20px;
    margin: 0 auto;
}

#container #lsloading {
    position: relative;
    top: 5px;
}

#container input {
    font-family: "Trebuchet MS";
    font-size: 1em;
    width: 350px;
    margin-left: 20px;
}

#container #lsresults {
    position: absolute;
    background: #fff;
    color: #333;
    width: 354px;
    margin-left: 20px;
}

#container #lsresults ul {
    list-style: none;
    margin: 0px;
    padding: 0px;
}

#container #lsresults ul li {
    list-style: none;
    margin: 10px;
    padding: 0px;
}</pre>
<h3>Javascript</h3>
<p>Javascriptet er den største del af denne &#8220;applikation&#8221;. JS-delen består af et Live Search objekt (<span style="font-family: Courier New;">ls</span>) med metoderne <span style="font-family: Courier New;">init()</span>, <span style="font-family: Courier New;">search()</span>, <span style="font-family: Courier New;">showResults()</span> og <span style="font-family: Courier New;">hideResults()</span>. Derudover bliver der, når en side er loaded,  initialiseret nogle observer eventhandlers på søgefelter.</p>
<p>Humlen ligger i <span style="font-family: Courier New;">init()</span> og <span style="font-family: Courier New;">search()</span> metoderne. <span style="font-family: Courier New;">init()</span> bliver kaldt hver gang der tastes i søgefeltet, og denne kalder <span style="font-family: Courier New;">search()</span> som udføre et AJAX-kald til serveren, som levere selve søgningen. For ikke at spamme serveren er der indbygget en timer i init() metoden som gør, at <span style="font-family: Courier New;">search()</span> metoden kun bliver kaldt hvis der ikke er tastet noget i søgefeltet i 0.5 sekunder.</p>
<pre>/**
 * Live search
 */
var ls = {
	queryField: "lsquery", //id name of queru input-field
	results: "lsresults", //id name of result-list
	loading: "lsloading", //id name for loading animation
	timer: null,

	/**
	 * Initialize a search:
	 * reset timer, and performe search
	 * if nothing has been typed in 0.5 sec.
	 */
	init: function(event) {
		var query = Event.element(event).value;
		ls.timer = clearTimeout(ls.timer);
		if(query != "" || query != "Type and you´ll find...") {
			ls.timer = setTimeout("ls.search(" query ");", 500);
		}
	},

	//start a search using AJAX
	search: function(query) {
		if(query != "") {
			$(ls.loading).show();
			var done = function(t) {
				$(ls.loading).hide();
			}
			var url = php-backend.php;
			var pars = action=search&amp;query=   query;
			new Ajax.Updater(lsResList, url,
			{
				method: post,
				parameters: pars,
				onSuccess:done,
				onFailure:done,
				inserting:Insertion.Before
			});
		}
	},

	//show the result list
	showResults: function() {
		if($(ls.results).getStyle(opacity) != 0.9)
			new Effect.Appear(ls.results, {duration:0.2, from:0.0, to:0.9});
	},

	//hide the result list
	hideResults: function() {
		new Effect.Opacity(ls.results, {duration:0.2, from:0.9, to:0.0});
	}
}

/**
 * Initialize key-observers for search input field.
 */
Event.observe(window, load, function() {
	$(ls.queryField).observe(keyup, ls.init);
	$(ls.queryField).observe(keydown, ls.init);

	//remove input field value when first focused
  	$(ls.queryField).onfocus = function() {
  		if($(ls.queryField).value == "Type and you´ll find...")
			$(ls.queryField).value = "";
		ls.showResults();
	}

	//reset input field when not focused and value is ""
  	$(ls.queryField).onblur = function() {
  		if($(ls.queryField).value == "")
  			$(ls.queryField).value = "Type and you´ll find...";
  		ls.hideResults();
  	}
});</pre>
<h3>PHP</h3>
<p>PHP delen, er hvad jeg ovenfor har kaldt &#8220;serveren&#8221;. Hvordan du vil udføre selve søgningen er mere eller mindre op til dig selv. Vælger du at benytte dette script, skal resultaterne dog returneres som én streng. Hvert resultat skal, for at designet holder stik, leveres som et <span style="font-family: Courier New;">&lt;li&gt;</span>-element.</p>
<p>Bare for at give en ide, leveres scriptet med følgende PHP backend.</p>
<pre>&lt;?php
/************************************************************************
 * Here you would probably perform som DB-search.
 * In this example, you will only find a hit, when typing a string that -
 * is in the array $results.
 ************************************************************************/
if($_POST["action"] == "search") :
	$query = utf8_decode(urldecode($_POST["query"]));
	$results = array("","webprogrammering","blog","ajax","javascript","php","css");
	$key = array_search($query, $results);
	if($key) :
		echo "&lt;li&gt;&lt;a href="#"&gt;"".utf8_encode($results[$key])."" found with key: ".$key."&lt;/a&gt;&lt;/li&gt;";
	else :
		echo "&lt;li&gt;&lt;i&gt;Intet resultat&lt;/i&gt;&lt;/li&gt;";
	endif;
endif;
?&gt;</pre>
<p>Som beskrevet i kommentaren, vil man selvfølgelig lave en reel database søgning eller lignende, hvilket lige nøjagtig er hvad jeg gør her på sitet.</p>
<p>Sååå &#8211; uden yderligere forklaringer skal du være velkommen til at downloade, modificere osv. følgende version af Live Search. Skulle der opstå problemer, eller mangler du yderligere forklaring, skal du endelig kommentere nedenfor.</p>
<p><a name="download"></a></p>
<h3>Download</h3>
<p><a title="Download &quot;Live search&quot; (Zip)" href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=3"><span style="text-decoration: line-through;">Live Search med Prototype JS og PHP</span></a><span style="text-decoration: line-through;"> (.zip)</span></p>
<p><a title="Download &quot;Live search&quot; (Zip)" href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=2">Live Search med Prototype JS og PHP</a> (Æ,Ø,Å understøttet) (.zip)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/live-search-med-prototype-js-og-php.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Blog Ping</title>
		<link>http://www.simon-jensen.net/blog-ping.html</link>
		<comments>http://www.simon-jensen.net/blog-ping.html#comments</comments>
		<pubDate>Mon, 21 Jan 2008 20:26:39 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ping]]></category>
		<category><![CDATA[rss]]></category>
		<category><![CDATA[xmlrpc]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/blog-ping.html</guid>
		<description><![CDATA[Du skriver en blog fordi du har noget at fortælle, samtidig vil du gerne at andre læser og kommenterer det du skriver &#8211; men hvordan får du dem ind på dit site? Dette må være et af de spørgsmål langt de fleste bloggere, online-shop-ejere, og andet online-folk, har spurgt sig selv om, fra tid til [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Du skriver en blog fordi du har noget at fortælle, samtidig vil du gerne at andre læser og kommenterer det du skriver &#8211; men hvordan får du dem ind på dit site</strong>?</p>
<p>Dette <span style="font-style: italic;">må</span> være et af de spørgsmål langt de fleste bloggere, online-shop-ejere, og andet online-folk, har spurgt sig selv om, fra tid til anden. Jeg siger ikke at hele svaret kommer i denne post, men af egne erfaringer vil jeg våge at påstå, at <a title="Lidt baggrunds læsning om RSS Feeds." href="http://www.simon-jensen.net/rss-til-alle.html">RSS</a> <span style="font-style: italic;">kan</span> have en hel del at sige. Måske du allerede vidste det, men en RSS feed giver dig ufatteligt mange muligheder for at distribuere din sides poster, produkter, nyheder eller hvad det ellers måtte være.</p>
<h3>RSS Feeds</h3>
<p>Snart alle blogs, og nu også de fleste public-service/offentlige-sites er ved at være med på vognen, når vi blot snakker om at have en RSS feed &#8211; hvad jeg detsvære ikke ved så meget om er, om disse sites også gør noget for at videre distribuere denne feed til andre services/medier.</p>
<p><span id="more-64"></span></p>
<p>En af de store forcer ved RSS feeds er, at de er formateret i XML. At en feed er formateret i XML betyder groft sagt bare, at alt det &#8220;designmæssige&#8221; bliver fjernet, hvorefter kun selve &#8220;indholdet&#8221; er tilbage og kan sendes videre. At en feed ikke indeholder noget design, og er i form af &#8220;ren tekst&#8221;, betyder at den efterfølgende er pændt nem at &#8220;transportere&#8221; og benytte i andre sammenhænge &#8211; som f.eks. kunne være på en mobiltelefon.</p>
<p>En RSS feed lader sig også forholdsvist nemt downloade, transportere og implementere fra site til site (også bare kaldet <em>parsing</em>), som jeg også beskrev i posten &#8220;<a title="Læs posten: &quot;RSS til alle&quot;." href="http://www.simon-jensen.net/rss-til-alle.html">RSS til alle</a>&#8221; &#8211; Parsing er f.eks. hvad du har mulighed for at gøre på Google´s personlige side <a title="Gå til http://www.google.dk/ig" href="http://www.google.dk/ig" target="_blank">iGoogle</a>, i din <a title="Gå til http://www.google.com/reader/" href="http://www.google.com/reader/" target="_blank">online feed reader</a> og mange andre steder.</p>
<h3>RSS/Ping Services</h3>
<p>Netop til parsing og visning af RSS feed, findes der adskillige dedikerede sites rundt omkring på nettet, og det er faktisk disse sites denne post reelt handler om, og som jeg vil prøve at gøre lidt reklame for.</p>
<p>Langt de fleste af disse sites har en såkaldt <strong>ping service</strong>. En ping service er, som navnet antyder, en service til at håndtere et såkaldet <strong>ping</strong>! Et ping er kort sagt en forespørgsel fra en computer til en anden. I RSS sammenhænge lyder denne forespørgsel nogenlunde således:</p>
<blockquote><p>Hej ping service &#8230; vil du ikke lige se om du kan finde noget nyt på min side. Adressen er <a href="http://www.simon-jensen.net">www.simon-jensen.net</a> &#8211; hilsen Simon Jensen.</p></blockquote>
<p>Herefter, hvis ellers pinget er formateret korrekt, vil ping servicen gennemgå dit site (feed), og se om der ikke godt nok skulle være kommet noget nyt, siden sidst denne fik et ping. Servicen finder din nye post/produkt, og et link, samt en lille beskrivelse, vil blive tilføjet til ping servicens side.</p>
<p>Du har nok fået fat i ideen &#8211; Det at sende et ping kan automatiseres, så det bare sker hver gang du opretter en post. Du kan lige ledes sende et ping til flere ping services. For hver service der optager din forespørgsel, kan du glæder dig over at have fået et indgående link &#8211; og dem vil vi jo gerne have <img src="http://www.simon-jensen.net/admin/editors/fckeditor/editor/images/smiley/msn/teeth_smile.gif" alt="" />. Se evt. denne side:</p>
<p><a title="Simon Jensens RSS Feed hos overskrift.dk" href="http://overskrift.dk/feed/bb5eb40b82e08c314b232b690e8157ba69137ab5" target="_blank">http://overskrift.dk/feed/bb5eb40b82e08c314b232b690e8157ba69137ab5</a></p>
<p>Der findes sågar ping services, som sørger for at sende din forespørgsel videre til andre ping services &#8211; man kommer selvsagt hurtigt ud til at mange &#8220;medier&#8221;. Her er et par links til lidt mere læsning om ping services &#8211; et par af dem, lader dig faktisk pinge direkte fra deres side af:</p>
<p><a href="http://blogbot.dk/info/xmlrpc.php" target="_blank">http://blogbot.dk/info/xmlrpc.php</a></p>
<p><a href="http://blogs.feedburner.com/feedburner/archives/000478.html" target="_blank">http://blogs.feedburner.com/feedburner/archives/000478.html</a></p>
<p><a href="http://www.overskrift.dk/about/ping.php" target="_blank">http://www.overskrift.dk/about/ping.php</a></p>
<p><a href="http://pingomatic.com/" target="_blank">http://pingomatic.com/</a></p>
<p><a href="http://codex.wordpress.org/Update_Services" target="_blank">http://codex.wordpress.org/Update_Services</a></p>
<p>De fleste af de mest udbredte blogging tools, har allerede indbygget funktionalitet til at sende et ping &#8211; men sidder du, som jeg, og leger med din egen blog (i PHP vel og mærket), har jeg tastet følgende ping-klasse sammen (med hjælp fra en implementering hos Blogbot.dk), som du skal være velkommen til at benytte som du vil.</p>
<h3>class.PING.php &#8211; PHP Ping klasse</h3>
<p>Du kan <a href="#download">downloade denne klasse</a>, samt et eksempelt nederst på denne side.</p>
<pre>&lt;?php
/**
 * Filename: class.PING.php
 *
 * @author Simon Jensen @ http://www.simon-jensen.net
 */

class PING
{
	/**
	 * URLs to ping.
	 * @var array
	 */
	public $pingUrls = array(
		"http://www.overskrift.dk/ping/",
		"http://blogbot.dk/io/xml-rpc.php",
		"http://rpc.pingomatic.com/",
		"http://rpc.weblogs.com/RPC2",
		"http://rpc.technorati.com/rpc/ping",
		"http://ping.weblogs.se/",
		"http://ping.feedburner.com",
		"http://api.feedster.com/ping"
		);

	/**
	 * Your website title here.
	 * @var string
	 */
	public $weblogName = "Your title goes here...";

	/**
	 * URL for your website.
	 * @var string
	 */
	public $weblogUrl = "http://localhost/Testing/ping/";

	/**
	 * Turn debugging on/off
	 * @var bool
	 */
	public $debug = false;

	/**
   	 * Default constructor
   	 *
   	 * @return PING
   	 */
  	function __PING() {}

  	/**
	 * Send a XLM-RPC ping to a specific URL.
	 *
	 * @param string $pingUrl - url to ping.
	 * @return string error msg.
	 */
	private function sendPing($pingUrl)
	{
	    $postdata = &lt;?xml version="1.0"?&gt;
	       &lt;methodCall&gt;
	         &lt;methodName&gt;weblogUpdates.ping&lt;/methodName&gt;
	         &lt;params&gt;
	           &lt;param&gt;&lt;value&gt;&lt;string&gt; .
	                preg_replace(|[x80-xFF]|e, "&amp;#".ord("").";", $this-&gt;weblogName) .
	                &lt;/string&gt;&lt;/value&gt;&lt;/param&gt;
	           &lt;param&gt;&lt;value&gt;&lt;string&gt; . htmlspecialchars($this-&gt;weblogUrl) . &lt;/string&gt;&lt;/value&gt;&lt;/param&gt;
	         &lt;/params&gt;
	       &lt;/methodCall&gt;;

	    $timeout = 20;

	    $parts = parse_url($pingUrl);
	    if (!isset($parts[host])) :
	        return array(-1, "Malformed URL:
" . $pingUrl);
	    endif;

	    $host = $parts[host];
	    $port = isset($parts[port]) ? $parts[port] : 80;
	    $path = isset($parts[path]) ? $parts[path] : /;
	    if (isset($parts[query])) :
	        $path .= ? . $parts[query];
	    endif;

	    $fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
	    if (!$fp) :
	        return array(-2, "Could not connect to $host:$port");
	    endif;

	    socket_set_timeout($fp, $timeout);

	    $request = "POST $path HTTP/1.0
" .
	        "Host: $host
" .
	        "Content-Type: text/xml
" .
	        "User-Agent: Aggemam XML-RPC client
" .
	        "Content-Length: " . strlen($postdata) . "
" .
	        "
" .
	        $postdata;
	    fputs($fp, $request);

	    if ($this-&gt;debug) :
	        print "&lt;div style=color: blue; white-space: pre&gt;";
	        print htmlspecialchars($request);
	        print "&lt;/div&gt;";
	    endif;

	    $response = ;
	    while (!feof($fp)) :
	        $response .= fgets($fp, 1024);
	        $status = socket_get_status($fp);

	        if ($status[timed_out]) :
	            fclose($fp);
	            return array(-3, "Request timed out");
	        endif;
	    endwhile;

	    fclose($fp);

	    if ($this-&gt;debug) :
	        print "&lt;div style=color: green; white-space: pre&gt;";
	        print htmlspecialchars($response);
	        print "&lt;/div&gt;";
	    endif;

	    if (preg_match(|&lt;name&gt;flerror&lt;/name&gt;s*&lt;value&gt;s*&lt;boolean&gt;([^&lt;])&lt;/boolean&gt;s*&lt;/value&gt;|, $response, $reg1) &amp;&amp; preg_match(|&lt;name&gt;message&lt;/name&gt;s*&lt;value&gt;(s*&lt;string&gt;)?([^&lt;]*)(&lt;/string&gt;s*)?&lt;/value&gt;|, $response, $reg2)) :
	        return array($reg1[1], $reg2[2]);
	    else :
	        return array(-3, "Malformed reply:
" . $response);
	    endif;
	}

	/**
	 * Send a XLM-RPC pings to each URL in $this-&gt;pingURLs
	 *
	 * @return string error msg.
	 */
	public function sendPings()
	{
		foreach ($this-&gt;pingUrls as $pingUrl) :
		    print "&lt;p&gt;Pinging $pingUrl ... ";
		    flush();

		    list($error, $message) = $this-&gt;sendPing($pingUrl, $this-&gt;weblogName, $this-&gt;weblogUrl, $this-&gt;debug); 

		    if ($error == 0) :
		        print "Ping recieved!&lt;/p&gt;";
		    else :
		        print "Something went wrong:&lt;/p&gt;";
		        print "&lt;p style=font-size: smaller; color: red; white-space: pre&gt;" . htmlspecialchars($message) . "&lt;/p&gt;";
		    endif;
		endforeach;
	}
}
?&gt;</pre>
<h3>Eksempel</h3>
<p>Følgende eksempel findes også i .zip-filen, som du kan downloade herunder.</p>
<pre>require_once("class.PING.php");
$ping = new PING();

/**
 * Turn debuggin on/off
 */
$ping-&gt;debug = true;

/**
 * You can also just edit the variables in the class
 */
$ping-&gt;weblogName = "Your website title here";
$ping-&gt;weblogUrl = "http://your-site-here.com";

/**
 * Overwrite the URLs to ping if desired.
 */
//$ping-&gt;pingUrls = array("http://rpc.pingomatic.com/", http://www.overskrift.dk/ping/");

/**
 * Send a ping to all URLs in $ping-&gt;pingUrls.
 */
$ping-&gt;sendPings();</pre>
<p><a name="download"></a></p>
<h3>Download</h3>
<p align="center"><a href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=6"><img title="Download" src="http://www.simon-jensen.net/admin/uploads/image/download.png" alt="Download" width="119" height="104" align="absMiddle" /></a> <a title="Download class.PING.php" href="http://www.simon-jensen.net/admin/plugins/download_manager/download.php?ID=6">class.PING.php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/blog-ping.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sådan oversætter du din webapplikation</title>
		<link>http://www.simon-jensen.net/saadan-oversaetter-du-din-webapplikation.html</link>
		<comments>http://www.simon-jensen.net/saadan-oversaetter-du-din-webapplikation.html#comments</comments>
		<pubDate>Fri, 26 Oct 2007 15:58:55 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[gettext]]></category>
		<category><![CDATA[gnu]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/saadan-oversaetter-du-din-webapplikation.html</guid>
		<description><![CDATA[For noget tid siden fik jeg til opgave at lave en multisproget applikation, m.o.a. et site som skulle kunne vises i flere forskellige sprog. Da jeg aldrig havde prøvet dette før, brugte jeg forholdsvis lang tid på at tænkt over hvordan den bedste og letteste måde, at gøre dette på, ville være. Som en hver [...]]]></description>
			<content:encoded><![CDATA[<p>For noget tid siden fik jeg til opgave at lave en multisproget applikation, m.o.a. et site som skulle kunne vises i flere forskellige sprog. Da jeg aldrig havde prøvet dette før, brugte jeg forholdsvis lang tid på at tænkt over hvordan den bedste og letteste måde, at gøre dette på, ville være. Som en hver anden sikkert ville gøre det, spurgte jeg mig frem, og så på hvordan andre applikationer implementerede flere sprog. Jeg så bla. på <a title="Gå til osCommerce" href="http://www.oscommerce.com/" target="_blank">osCommerce</a>, og ASP.Net forummet <a title="Gå til Yet Another Forum." href="http://www.yetanotherforum.net/" target="_blank">Yet Another Forum</a>, som jeg for nyligt havde arbejdet med &#8211; men ingen af dem synes at håndterer denne opgave specielt fordelagtige. Efter at have være igennem et par applikationer mere, og snakket flere forskellige løsninger igennem med andre, faldt jeg endelig over en løsning, som synes at være smart.</p>
<p>Jeg skal fra start af være ærlig og give massere af credit til <a title="Gå til Think Vitamin." href="http://www.thinkvitamin.com" target="_blank">Think Vitamin</a>, for deres artikkel omkring internationalisering af websites, da denne post ikke var blevet til uden at jeg havde læst deres først (<em>backtrack med ord <img src='http://www.simon-jensen.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </em>).</p>
<p>Først vil jeg starte ud med at fortælle lidt om den metode som jeg havde i tankerne i starten af forløbet, og forklare lidt om hvorfor det ikke blev denne vi anvendte i sidste ende. Hvis du blot vil læse om den &#8220;optimale måde&#8221;, kan du springe dette skridt over, og <a href="http://www.simon-jensen.net/saadan-oversaetter-du-din-webapplikation.html#gettext">gå direkte til gettext-metoden</a>.</p>
<p><span id="more-82"></span></p>
<h3>En ikke helt optimal metode</h3>
<p>En af de første metoder jeg var inde på, var <em>associative arrays</em> metoden. En metode hvor du, for hvert sprog i din applikation, opretter et array. Arrayet får som index den streng der skal oversættes, og som værdi den oversatte streng.</p>
<p>Følgende eksempel viser hvordan du ville kunne bruge associative arrays, til at implementerer strenge på dansk og engelsk. For at udvide med flere sprog, skal du blot oprette endnu et array, og tilføje cases til switch-funktionen:</p>
<div class="Code">//sprog setup<br />
$lang = array(); //skal senere holde det valge sprog-array</p>
<p>$english = array();<br />
$english["Klik for at fortsætte"] = &#8220;Click to continue&#8221;;</p>
<p>$dansk = array();<br />
$dansk["Klik for at fortsætte"] = &#8220;Klik for at fortsætte&#8221;;</p>
<p>//sprog bestemmes efter en URL-parameter ?lang=xy<br />
switch($_GET["lang"]) {<br />
case &#8220;en&#8221;:<br />
$lang = $english;<br />
break;</p>
<p>default:<br />
$lang = $dansk;<br />
break;<br />
}</p>
<p>//benyt det valge sprog-array<br />
echo $lang["Klik for at fortsætte"];</p></div>
<p>Det lader jo til at virker meget fint &#8211; og det gør det da også langt hen ad vejen. Men hvad nu hvis ud kommer til at lave en stavefejl? Hvis du f.eks. glemmer et &#8220;t&#8221; i &#8220;fortsætte&#8221;, når du vil udskrive den oversatte tekst. Tja &#8211; Alt efter hvilket programmeringssprog du benytter, vil der opstå problemer af en eller anden art. Den mildeste heraf vil blot undlade at vise nogen tekst overhovedet, da der jo er angivet et ikke-eksisterende index = ingen værdi/tekst. I værste fald vil hele din applikation kaste en fejl, og siden vil ikke være brugbar. Dine brugere vil højst sansynligt blive efterladt i total forviring, og sitet synes pludseligt at forekomme rimelig uprof!</p>
<p>En anden ting, som kan være besværlig, ved brugen af associative arrays, er vedligeholdelse. Det kan hurtigt blive uoverskueligt at skulle finde/opdaterer/slette eller oprette en enkelt streng i alle &#8220;sprogene&#8221; &#8211; og det kommer man jo nok ikke udenom! Forestil dig at du har bare 100 strenge, af forskellige længder, som du skal have i flere forskellige sprog. Du kan selv lave den danske, muligvis andre, men hvordan vil du præsenterer dette for din oversætter? Det er ikke sikkert at denne har et pind forstand på programmering, og bare synet af et array vil skræmme personen og få opgaven til at virke liiiidt for kompliceret! Du kunne så selvfølgelig vælge at skrive hvert streng på en linie i et andet dokument, men så skal du til at kopiere/indsætte alle strengene på den rigtige plads i arrayet &#8211; for hvert eneste sprog! Naahhh&#8230;</p>
<p>Okay &#8211; jeg tror jeg har gjort det klart nok. Associative arrays er ikke den mest optimale løsning &#8211; men hvad er det så? Som den PHP-nørd jeg nu en gang er endt ud med at blive, søgte jeg selvfølgelig svaret i dette sprog &#8211; Og hvad jeg kom frem til var det dedikerede GNU Projekt <a title="Gå til: http://www.gnu.org/software/gettext/" href="http://www.gnu.org/software/gettext/" target="_blank"><span style="font-family: 'Courier New';">gettext</span></a> (som vidst også kan findes til andet end PHP).</p>
<p><a name="gettext"></a></p>
<h3>Oversættelse med gettext</h3>
<p>Nu har jeg jo været inde på nogle af de problematikker der kan ligge i f.eks. at bruge associative arrays, så for at gøre en lang historie kort, så kommer du selvfølgelig ud over disse ved at benytte gettext. Gettext introducerer samtidig en fantastisk måde at adskille kode fra indhold (læs: tekst), og når det endelig er sat op, er det enormt nemt at vedligeholde.</p>
<p>Ved at gennemfører følgende 6 trin, kan du for alvor nyde effekten af gettext:</p>
<ol>
<li>Den tekst du skal have oversat skal parses gennem PHP-funktionen <span style="font-family: 'Courier New';">gettext()</span>.</li>
<li>Efter at alt tekst der skal oversættes er blevet parset bruges kommandoen xgettext som opretter et PO (Portable Object), hvilket i bund og grund er et rent tekst dokument.</li>
<li>Du sendt PO filen til din oversætter, som kan oversætte teksten med en PO-editor.</li>
<li>Din oversætter sender PO filen tilbage, nu indeholdende din tekst plus den oversatte tekst.</li>
<li>Du compiler PO filen til en MO fil (Machine Object) som kan læses af <span style="font-family: 'Courier New';">gettext()</span> funktionen.</li>
<li>Du opsætter din locale (sprog opsætning for din applikation) og kan nu nyde din side i alle de oversatte sprog.</li>
</ol>
<p><strong>Gettext step by step</strong></p>
<p><strong>1). Benyt <span style="font-family: 'Courier New';">gettext()</span> funktionen:</strong></p>
<p>Det første punkt er nok det mest bøvlede, om ikke andet så det der vil tage mest tid. Ikke desto mindre er det ganske simpelt, men blot kedeligt. En streng som følgende:</p>
<div class="Code">&lt;p&gt;Denne tekst skal oversættes.&lt;/p&gt;</div>
<p>Skal blive til:</p>
<div class="Code">&lt;p&gt;&lt;?php echo <strong>gettext(</strong>&#8220;Denne tekst skal oversættes&#8221;<strong>);</strong> ?&gt;.&lt;/p&gt;</div>
<p>Dette skal som sagt gøres for alle strenge du vil have sendt med til din oversætter.</p>
<p><strong>2). Oprettelse af PO filen:</strong></p>
<p>I dette skridt skal du have gang i programmet xgettext, hvilket skal bruges til at løbe alle dine PHP-filer igennem, for at udtrække de strenge du har benyttet <span style="font-family: 'Courier New';">gettext()</span>-funktionen på. Alle de fundne strenge vil blive samlet i en PO fil, som du kan sende afsted til oversættelse. En PO fil er i bund og grund blot en tekstfil tilføjet lidt ekstra informationer.</p>
<p>Benytter du Linux er det sandsynligt at du allerede har xgettext installeret, alternativt vil det ikke være svært at få fat i. Du kan evt. installerer gettext-modulet ved følgende kommando (eller benyt din egen packagemanager):</p>
<div class="Code">sudo apt-get install gettext</div>
<p>Alternativt findes PO-editoren <a title="Gå til poedit." href="http://www.poedit.net/" target="_blank">poedit</a> til både Windows, Mac og Linux. Til Windows kommer denne editor med alle de nødvendige hjælpeprogrammer til at kompleterer oversættelsen. Mens selve oversættelsen ikke er afhængig af poedit, kan programmet være meget anvendeligt alligevel, da det giver en nem og overskuelig måde for både at oversætte, kompilerer og vedligeholde.</p>
<p>Tager vi udgangspunkt i opretelsen af PO filen fra selve editoren poedit, er det nødvendigt at definerer et par indstillinger. Ved opretelse af et nyt &#8220;katalog&#8221; (som det kaldes på dansk) skal du under &#8220;stier&#8221; definerer kataloget som udgør roden i din applikation. Dersuden skal du udfylde nogle standardfelter (Projektinformation) som bruges rent informativt i PO filen. Når disse indstillinger er sat, vil du blive bedt om at gemme din PO fil. Første gang du gemmer denne vil poedit automatisk hente de strenge du har valgt skal med til oversættelse &#8211; herefter kan du til hver en tid opdaterer din PO fil ved blot at trykke på &#8220;Opdaterings katalog&#8221;-knappen.</p>
<p><a title="Her skal du angive roden i din applikation." rel="lightbox" href="http://www.simon-jensen.net/admin/uploads/image/gettext/poedit-indstillinger.png"><img title="Her skal du angive roden i din applikation." src="http://www.simon-jensen.net/admin/uploads/image/gettext/poedit-indstillinger.png" alt="Her skal du angive roden i din applikation." width="225" height="179" /></a> <a title="Dette er hvad du vil se, når du har opdateret dit katalog." rel="lightbox" href="http://www.simon-jensen.net/admin/uploads/image/gettext/poedit-new-strings.png"> <img title="Dette er hvad du vil se, når du har opdateret dit katalog." src="http://www.simon-jensen.net/admin/uploads/image/gettext/poedit-new-strings.png" alt="Dette er hvad du vil se, når du har opdateret dit katalog." width="224" height="170" /></a></p>
<p>Alternativt skal du have gang i kosollen, hvilket dog heller ikke er svært. For at oprette en PO fil, som samler strenge fra alle PHP-filer i flere kataloger, skal du blot bruge følgende kommando:</p>
<div class="Code">xgettext -o messages.po *.php ./andet-katalog/*.php</div>
<p>Herefter vil du, i det katalog hvod du udfører kommandoen, få oprettet din PO fil.</p>
<p><strong>3 &amp; 4). Send PO filen til din oversætter</strong></p>
<p>Som jeg nævnte tidligere virker det ikke særlig fordelagtigt at skulle oversætte direkte i en tekstfil, og da slet ikke hvis man skal sidde og passe sine strenge ind i et array. PO filer er heller ikke specielt pæne at skulle redigerer direkte i, selvom det kan lade sig gøre &#8211; men hvilken fil er det? Din oversætter bør derfor få fat i en PO editor, som f.eks. poedit som er ganske gratis. Herefter er oversættelsen let som en leg &#8211; Hvad din oversætter vil se, er hvad du ser på billedet herunder:</p>
<p><a title="Oversættelse med poedit er lige til!" rel="lightbox" href="http://www.simon-jensen.net/admin/uploads/image/gettext/poedit-workspace.png"><img title="Oversættelse med poedit er lige til!" src="http://www.simon-jensen.net/admin/uploads/image/gettext/poedit-workspace.png" alt="Oversættelse med poedit er lige til!" width="225" height="212" /></a></p>
<p>Oversætteren oversætter samtlige strenge, og sender den oversatte PO fil tilbage til dig.</p>
<p><strong>5). Compiling af PO til MO</strong></p>
<p>For at compile PO filen til en MO fil, som er den fil PHP bruger for at oversætte strenge der er benyttet <span style="font-family: 'Courier New';">gettext()</span>-funktionen på, skal vi have gang i msgfmt programmet. msgfmt komme ligeledes med poedit til Windows, og til Linux ved at installerer hele gettext modulet. Igen er det meget nemmere ved brugen af poedit, og faktisk bliver dette skridt automatisk udført allerede hos din oversætter, men for en god ordens skyld tager vi det lige med her.</p>
<p>Når du har modtaget din PO fil fra oversætteren skal du blot åbne denne i poedit og trykke &#8220;gem&#8221;! Jeps, det var det, så har du oprettet en MO fil. vil du benytte kommandoprompten, skal du blot udfører følgende:</p>
<div class="Code">msgfmt navnet-på-din-po-fil.po</div>
<p><strong>6). Sprog opsætning i din applikation:</strong></p>
<p>Foruden at skulle finde alle de mange strenge og pakke dem ind i <span style="font-family: 'Courier New';">gettext()</span>-funktionen, så var dette sidste skridt faktisk det eneste der gav mig en smule problemer. Problemerne grunder i at dette skridt kan være afhængig af din webserver &#8211; så frygt ikke hvis ikke du får det til at virke i første omgang (spørg evt. din udbyder til råds).</p>
<p><img src="http://www.simon-jensen.net/admin/uploads/image/gettext/lc-messages.png" alt="" width="165" height="189" align="right" />Først og fremmest skal du have opbygget en filstruktur, som vist på billedet til højre. Jeg har valgt at ligge alle mine sprog i en overordnet mappe kaldet &#8220;languages&#8221; &#8211; navnet på denne mappe kan du selv bestemme, men navnet skal bruges som et &#8220;domæne&#8221;, som vist lige om lidt.</p>
<p>I dette eksempel har jeg kun taget to sprog med, skal du have flere sprog i din applikation, skal du oprette en mappe for hver af disse, hver mappe navngives med sprogets locale-code (F.eks. &#8220;en_US&#8221; for engelsk, &#8220;da_DK&#8221; for dansk osv.). Inde i hvert af disse mapper opretter du endnu en mappe som altid hedder &#8220;LC_MESSAGES&#8221;. Som det også er vidst på billedet, skal du placerer MO-filerne i LM_MESSAGES mapperne, jeg har min tilsvarende PO filer liggende i samme mappe, dette er ikke nødvendigt, men blot overskueligt.</p>
<p>Den allersidste del der skal til, er et par linier kode, og så er du faktisk kørende. Tilføj følgende linier i evt. i en fil du du altid kan inkludere:</p>
<div class="Code">&lt;?php<br />
//få fat i det valgte sprog<br />
$local = $_GET["lang"];<br />
if($local == &#8220;&#8221;) :<br />
$local = &#8220;da_DK&#8221;;<br />
endif;</p>
<p>//definer serverens &#8220;locale&#8221;<br />
putenv(&#8220;LC_ALL=$local&#8221;);<br />
setlocale(LC_ALL, $local);</p>
<p>//lås domænet &#8220;messages&#8221; til biblioteket &#8220;languages&#8221;<br />
bindtextdomain(&#8220;messages&#8221;, &#8220;/absolute/path/to/languages/&#8221;);</p>
<p>//bestem hvordan din oversatte tekst skal returneres, ændre evt. til iso-8859-1<br />
bind_textdomain_codeset(&#8220;messages&#8221;,&#8221;UTF-8&#8243;);</p>
<p>//bestem domæne-navn, betyder at gettext vil se efter filer kaldet messages.mo<br />
textdomain(&#8220;messages&#8221;);<br />
?&gt;</p></div>
<p>I eksemplet ovenfor, kan vi skifte sprog vha. en query streng. Personligt har jeg brugt cookies, men dette valg er jo op til dig. En af de rigtig fede ting ved denne metode er, at din applikation <em>altid</em> vil udskrive noget. Bilver <span style="font-family: 'Courier New';">$local</span> variablen i ovenstående eksempel ikke sat, eller bliver den sat til noget der ikke giver mening, vil gettext altid udskrive dit originale sprog &#8211; i mit tilfælde dansk.</p>
<p>Puhaa &#8211; det var en lang smøre, men skulle du af en eller anden grund ikke have fået nok, kan du evt. læse mere om gettext på følgende URLer:</p>
<p><a title="Gå til http://dk.php.net/gettext." href="http://dk.php.net/gettext" target="_blank">http://dk.php.net/gettext</a></p>
<p><a title="Gå til http://www.gnu.org/software/gettext/." href="http://www.gnu.org/software/gettext/" target="_blank">http://www.gnu.org/software/gettext/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/saadan-oversaetter-du-din-webapplikation.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL 5.0 og PHP 4</title>
		<link>http://www.simon-jensen.net/mysql-5-og-php-4.html</link>
		<comments>http://www.simon-jensen.net/mysql-5-og-php-4.html#comments</comments>
		<pubDate>Fri, 06 Jul 2007 06:43:20 +0000</pubDate>
		<dc:creator>Simon Jensen</dc:creator>
				<category><![CDATA[Udvikling]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.simon-jensen.net/MySQL-5.0-og-PHP-4.html</guid>
		<description><![CDATA[Jeg har lige haft formateret min computer. I den forbindelse skulle jeg &#8220;selvfølgelig&#8221; have geninstalleret PHP og MySQL, og i samme forbindelse støtte jeg på et lille problem, som det lader til flere har støt på (at dømme ud fra googles resultater). Problemet Jeg havde en applikation, som virkede fint lige før jeg formaterede computeren, [...]]]></description>
			<content:encoded><![CDATA[<p>Jeg har lige haft formateret min computer. I den forbindelse skulle jeg &#8220;selvfølgelig&#8221; have geninstalleret PHP og MySQL, og i samme forbindelse støtte jeg på et lille problem, som det lader til flere har støt på (at dømme ud fra <a title="Google søgning på understående problem." href="http://www.google.dk/search?hl=da&amp;client=firefox-a&amp;rls=org.mozilla%3Ada%3Aofficial&amp;hs=Cxn&amp;q=Warning%3A mysql_connect%28%29 %5Bfunction.mysql-connect%5D%3A Client does not support authentication protocol requested by server%3B consider upgrading MySQL client &amp;btnG=S%C3%B8g&amp;meta=" target="_blank">googles resultater</a>).</p>
<p><strong>Problemet</strong></p>
<p>Jeg havde en applikation, som virkede fint lige før jeg formaterede computeren, men efter at have geninstalleret PHP og MySQL, kunne jeg ikke få forbindelse til databasen mere. Jeg havde ligeledes installeret <a title="Download MySQL GUI Tools" href="http://dev.mysql.com/downloads/gui-tools/5.0.html" target="_blank">MySQL GUI Tools</a>, hvilke jeg brugte til at oprette brugere til databasen. Det skal nævnes at jeg denne gang installerede <strong>PHP v. 4.4.4</strong> og <strong>MySQL v. 5.0.27</strong>, hvorefter jeg fik følgende fejl:</p>
<div class="Code">Warning: mysql_connect() [function.mysql-connect]: Client does not support authentication protocol requested by server; consider upgrading MySQL client</div>
<div class="Code"><span id="more-75"></span></div>
<p><strong>Hvorfor?</strong></p>
<p>Problemet ligger i en ny måde at verificerer passwords på i MySQL 5.0 databasen &#8211; her snakker vi passwords til bruger af selve MySQL databasen, ikke af et CMS eller anden web applikation. Før formateringen brugte jeg MySQL 4.x.x samt PHP v. 4, hvilke arbejder meget  godt sammen. Men PHP 4 benytter &#8220;en gammel MySQL klient&#8221;, som ikke umiddelbart kan arbejde sammen med MySQL 5.0 pga. den nye måde at verificerer passwords på.</p>
<p><strong>Løsningen</strong></p>
<p>Løsningen er heldigvis til at komme over &#8211; du skal blot opdaterer dine passwords til dine MySQL brugere!</p>
<p>Når du har installeret MySQL databasen og oprettet dine brugere, kan du logge ind gennem MySQL kommandoprompten, og opdaterere dine brugere. Du finder kommandoprompten ved at kører mysql.exe fra &#8220;bin&#8221; kateloget, hvor du har installeret serveren.</p>
<p>Start mysql.exe med root brugeren:</p>
<div class="Code">C:mysql-5.0.27-win32in&gt;mysql -u root</div>
<p>Benyt databasen med brugere af serveren (denne er standard):</p>
<div class="Code">mysql&gt; use mysql;</div>
<p>Opdater dine brugeres passwords til at være kompatible med ældre klienter:</p>
<div class="Code">mysql&gt; UPDATE user SET Password=<strong>OLD_PASSWORD</strong>(&#8220;dit-password-her&#8221;) WHERE User=&#8221;din-bruger-her&#8221;;</p>
<p>mysql&gt; FLUSH PRIVILEGES;</p></div>
<p>Du kan læse mere om problemet på <a title="MySQL Developer Zone." href="http://dev.mysql.com/doc/refman/5.0/en/old-client.html" target="_blank">MySQL developer zone</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simon-jensen.net/mysql-5-og-php-4.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

