<?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/"
xmlns:series="http://unfoldingneurons.com/"
> <channel><title>Technik, Gothic und Anderes &#187; Theorie und Schnipsel</title> <atom:link href="http://blog.oncode.info/category/coding/theorie/feed/" rel="self" type="application/rss+xml" /><link>http://blog.oncode.info</link> <description>Technik ist Spiel, Gothic ist ernst und Zeit hat man zuviel</description> <lastBuildDate>Wed, 01 Feb 2012 17:03:41 +0000</lastBuildDate> <language>de</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Upload und Speichern von Dateien in einer DB mit einer C# ASP.NET MVC Applikation</title><link>http://blog.oncode.info/2012/02/01/upload-und-speichern-von-dateien-in-einer-db-mit-einer-c-asp-net-mvc-applikation/</link> <comments>http://blog.oncode.info/2012/02/01/upload-und-speichern-von-dateien-in-einer-db-mit-einer-c-asp-net-mvc-applikation/#comments</comments> <pubDate>Wed, 01 Feb 2012 17:03:41 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[Webapplikationen]]></category> <category><![CDATA[asp]]></category> <category><![CDATA[asp.net]]></category> <category><![CDATA[bild]]></category> <category><![CDATA[Datenbank]]></category> <category><![CDATA[file]]></category> <category><![CDATA[html5]]></category> <category><![CDATA[mvc]]></category> <category><![CDATA[upload]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1917</guid> <description><![CDATA[Hier wird beschrieben, wie man verschiedene Dateitypen (Bilder, Audio, Video, ...) in einer MVC 3 Webapplikation hochladen, in einer Datenbank speichern und auch wieder abspielen kann.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2012/02/upload.png" alt="upload" title="upload" width="130" height="155" class="lead" align="left" />MVC mit C# macht ja grunds&#228;tzlich Spass (obwohl f&#252;r einige Leute ja auch Auspeitschen angenehm ist <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). Ich habe ein paar spezielle Aufgaben zum Realisieren gefasst und gebe hier ein paar Weisheiten von mir, die wahrscheinlich allgemein bekannt sind und ich einfach nur nicht finden kann.</p><p>In diesem Beitrag wird beschrieben, wie man verschiedene Medien (Bilder, Filme, Audio) hochladen, in einer Datenbank speichern und (mit HTML 5) wieder darstellen kann. Ich gehe aus von einer frisch erstellten und herausgeputzten MVC 3 Webapplikation.</p><p>Am Schluss sollte die Liste in etwa so aussehen:</p><p><a
href="http://blog.oncode.info/wp-content/uploads/2012/02/mediapoc.png"><img
src="http://blog.oncode.info/wp-content/uploads/2012/02/mediapoc-152x300.png" alt="Liste der Medien" title="mediapoc" width="152" height="300" class="alignnone size-medium wp-image-1928" /></a></p><h3>M wie Model</h3><p>Mein Model speichert einen Namen, den Content- (oder MIME-) Type und die bin&#228;ren Daten des Mediums. Aus dem folgenden Model macht das Entity-Framework ein erstaunlich gutes SQL-Schema:<br
/> [cc lang="C#"]<br
/> // Sie ist ein Model und sie sieht gut aus&#8230;<br
/> namespace MediaPOC.Models {<br
/> public class Media {</p><p> public virtual int MediaId { get; set; }<br
/> [Required]<br
/> [StringLength(150)]<br
/> public virtual string Name { get; set; }<br
/> [Required]<br
/> [StringLength(250)]<br
/> [ScaffoldColumn(false)]<br
/> public virtual string MimeType { get; set; }<br
/> [Required]<br
/> public virtual byte[] Data { get; set; }<br
/> }<br
/> }<br
/> [/cc]</p><p>Daraus kann man nun den Rest mal scaffolden. Das Produkt wird aber noch nicht ganz funktionieren, da sich MVC 3 keinen Reim auf das &#8220;Data&#8221; Feld machen kann.</p><h3>V wie View</h3><p>Der grunds&#228;tzliche Trick besteht darin, dem file-upload-Tag einen anderen Namen zu geben um so die Datei ganz kontrolliert im Model abspeichern zu k&#246;nnen. Ebenfalls muss der Form-Tag f&#252;r den Upload angepasst werden. Die wichtigen Teile von <tt>Create.cshtml</tt> (und ev. auch <tt>Edit.cshtml</tt>):<br
/> [cc lang="html"]<br
/> @using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = &#8220;multipart/form-data&#8221; })) {<br
/> @Html.ValidationSummary(true)</p><fieldset><legend>Media</legend><div
class="editor-label"> @Html.LabelFor(model => model.Name)</div><div
class="editor-field"> @Html.EditorFor(model => model.Name)<br
/> @Html.ValidationMessageFor(model => model.Name)</div><div
class="editor-label"> @Html.LabelFor(model => model.Data)</div><div
class="editor-field"> <input
type="file" name="file" /> @Html.ValidationMessageFor(model => model.Data)</div> <input
type="submit" value="Create" /></fieldset><p>}<br
/> [/cc]</p><h3>C wie Controller</h3><p>Die Datei wird dem Controller als zus&#228;tzliches Argument &#252;bergeben. Ein Grundproblem ist, dass das Model wegen dem leeren Date ung&#252;ltig ist. Ein Revalidate nach dem Speichern der Daten bes&#228;nftigen die MS-G&#246;tter:<br
/> [cc lang="C#"]<br
/> //<br
/> // POST: /Home/Create</p><p> [HttpPost]<br
/> public ActionResult Create(Media media, HttpPostedFileBase file)<br
/> {<br
/> // Do we have a file<br
/> if (file != null &#038;&#038; file.ContentLength > 0)<br
/> { // Yes<br
/> media.MimeType = file.ContentType;<br
/> media.Data = new byte[file.ContentLength];<br
/> file.InputStream.Read(media.Data, 0, file.ContentLength);<br
/> }<br
/> // If a file was supplied, we have saved the data in teh model<br
/> ModelState.Clear();<br
/> TryValidateModel(media);<br
/> if (ModelState.IsValid)<br
/> {<br
/> db.Media.Add(media);<br
/> db.SaveChanges();<br
/> return RedirectToAction(&#8220;Index&#8221;);<br
/> }</p><p> return View(media);<br
/> }<br
/> [/cc]</p><p>Und das wars schon.</p><h3>Moment, wie kriegen wir die Daten wieder raus?</h3><p>Nun, &#228;&#228;&#228;&#228;hm, ja. Ganz grunds&#228;tzlich braucht es sicher eine M&#246;glichkeit, die Daten dem Browser zu senden. Die folgende L&#246;sung ist einfach und nett, hat allerdings ein Problem: Der Dateiname bei einem Download ist h&#228;sslich.<br
/> [cc lang="C#"]<br
/> namespace MediaPOC.Controllers {<br
/> public class MediaController : Controller {<br
/> private MediaPOCContext db = new MediaPOCContext();</p><p> //<br
/> // GET: /Media/Show/5<br
/> public ActionResult Show(int id) {<br
/> Media media = db.Media.Find(id);<br
/> return File(media.Data, media.MimeType);<br
/> }<br
/> }<br
/> }<br
/> [/cc]</p><p>Um in den Views locker zu bleiben habe ich mir HTML5 zu Hilfe geholt. Ein View-Helper soll jeweils einen korrekten Media-Tag und ein Link darstellen. Bei Audio/Video wird das Element unsichtbar gemacht, bis man sich sicher ist, dass der Browser das Format beherrscht. Das Format des Mediums wird beim Tag in einem <tt>data-type</tt> Attribut gespeichert. Angewendet wird der Tag in einer View mittels:<br
/> [cc lang="HTML"]<br
/> @Html.MediaTag(mediaObject)<br
/> [/cc]</p><p>Ich weiss, der Code ist leicht redundant, aber ich wollte Audio und Video aus irgendeinem ganz wichtigen Grund, den ich vergessen habe, trennen:<br
/> [cc lang="C#"]<br
/> namespace MediaPOC.Helpers {<br
/> public static class HtmlHelpers {<br
/> public static MvcHtmlString MediaTag(this HtmlHelper helper, Media media, int thumbnailsize=50) {<br
/> UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);<br
/> string url = urlHelper.Action(&#8220;Show&#8221;, &#8220;Media&#8221;, new { id = media.MediaId });</p><p> TagBuilder mediaTag;<br
/> TagBuilder brTag= new TagBuilder(&#8220;br&#8221;);</p><p> string tag=&#8221;";</p><p> if(media.MimeType==&#8221;image/png&#8221; || media.MimeType==&#8221;image/gif&#8221; || media.MimeType== &#8220;image/jpeg&#8221;) {<br
/> TagBuilder imageTag = new TagBuilder(&#8220;img&#8221;);<br
/> imageTag.MergeAttribute(&#8220;src&#8221;, url);<br
/> imageTag.MergeAttribute(&#8220;class&#8221;, &#8220;thumbnail&#8221;);</p><p> mediaTag = new TagBuilder(&#8220;a&#8221;);<br
/> mediaTag.MergeAttribute(&#8220;href&#8221;, url);<br
/> mediaTag.InnerHtml = imageTag.ToString();<br
/> tag=mediaTag.ToString(TagRenderMode.Normal);</p><p> }<br
/> else if (media.MimeType.StartsWith(&#8220;audio/&#8221;))<br
/> {</p><p> TagBuilder aTag = new TagBuilder(&#8220;a&#8221;);<br
/> aTag.MergeAttribute(&#8220;href&#8221;, url);<br
/> aTag.InnerHtml = media.Name;</p><p> mediaTag = new TagBuilder(&#8220;audio&#8221;);<br
/> mediaTag.MergeAttribute(&#8220;data-type&#8221;, media.MimeType);<br
/> mediaTag.MergeAttribute(&#8220;style&#8221;, &#8220;display:none&#8221;);<br
/> mediaTag.MergeAttribute(&#8220;class&#8221;, &#8220;thumbnail&#8221;);<br
/> mediaTag.MergeAttribute(&#8220;src&#8221;, url);<br
/> mediaTag.MergeAttribute(&#8220;controls&#8221;, &#8220;controls&#8221;);<br
/> mediaTag.InnerHtml = aTag.ToString();<br
/> tag = mediaTag.ToString(TagRenderMode.Normal) + brTag.ToString()+aTag.ToString();</p><p> }<br
/> else if (media.MimeType.StartsWith(&#8220;video/&#8221;))<br
/> {</p><p> TagBuilder aTag = new TagBuilder(&#8220;a&#8221;);<br
/> aTag.MergeAttribute(&#8220;href&#8221;, url);<br
/> aTag.InnerHtml = media.Name;</p><p> mediaTag = new TagBuilder(&#8220;video&#8221;);<br
/> mediaTag.MergeAttribute(&#8220;data-type&#8221;, media.MimeType);<br
/> mediaTag.MergeAttribute(&#8220;style&#8221;, &#8220;display:none&#8221;);<br
/> mediaTag.MergeAttribute(&#8220;class&#8221;, &#8220;thumbnail&#8221;);</p><p> mediaTag.MergeAttribute(&#8220;src&#8221;, url);<br
/> mediaTag.MergeAttribute(&#8220;controls&#8221;, &#8220;controls&#8221;);<br
/> mediaTag.InnerHtml = aTag.ToString();<br
/> tag = mediaTag.ToString(TagRenderMode.Normal) + brTag.ToString() + aTag.ToString();</p><p> }<br
/> else<br
/> {<br
/> mediaTag = new TagBuilder(&#8220;a&#8221;);<br
/> mediaTag.SetInnerText(media.Name);<br
/> mediaTag.MergeAttribute(&#8220;href&#8221;, url);<br
/> tag = mediaTag.ToString(TagRenderMode.Normal);<br
/> }</p><p> return new MvcHtmlString(tag);<br
/> }<br
/> }//end of class<br
/> }//end of namespace<br
/> [/cc]</p><p>Um die entsprechenden Elemente sichtbar zu machen, wenn der Browser sie beherrscht, muss man noch etwas JavaScript absondern:<br
/> [cc lang="JavaScript"]<br
/> $(function () {<br
/> $(&#8220;audio&#8221;).each(function (index) {</p><p> var audioElement = document.createElement(&#8216;audio&#8217;);<br
/> var canPlayType = audioElement.canPlayType($(this).attr(&#8216;data-type&#8217;));</p><p> if (canPlayType.match(/maybe|probably/i)) {<br
/> $(this).show();<br
/> }</p><p> });<br
/> $(&#8220;video&#8221;).each(function (index) {</p><p> var videoElement = document.createElement(&#8216;video&#8217;);<br
/> var canPlayType = videoElement.canPlayType($(this).attr(&#8216;data-type&#8217;));</p><p> if (canPlayType.match(/maybe|probably/i)) {<br
/> $(this).show();<br
/> }</p><p> });<br
/> });<br
/> [/cc]</p><p>So, das wars. Super.</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2012/02/01/upload-und-speichern-von-dateien-in-einer-db-mit-einer-c-asp-net-mvc-applikation/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Programmierquiz: K&#252;rzesten, nicht passenden String finden, auf dass ein RegExp nicht passt</title><link>http://blog.oncode.info/2011/10/15/programmierquiz-kurzestes-nicht-passendes-wort-finden-auf-dass-ein-regexp-nicht-passt/</link> <comments>http://blog.oncode.info/2011/10/15/programmierquiz-kurzestes-nicht-passendes-wort-finden-auf-dass-ein-regexp-nicht-passt/#comments</comments> <pubDate>Sat, 15 Oct 2011 10:47:24 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[Aufgaben]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[RegExp]]></category> <category><![CDATA[Regular Expressions]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1905</guid> <description><![CDATA[Aufgabe: Finde den k&#252;rzesten String in einem gegebenen Alphabet, auf dass ein gegebener Regular Expression nicht passt.]]></description> <content:encoded><![CDATA[<p>Hier mal eine lustige Aufgabe, mit der ich vor Kurzem konfrontiert war: Gegeben sei ein Alphabet Σ mit Symbolen und eine Regular Expression, wie etwa <tt>(0*|1*)(0*|1*)(0*|1*)</tt>. Nun ist dar k&#252;rzeste String mit Symbolen aus dem Alphabet gesucht, auf dass der gegebene RegExp <em>nicht</em> passt.</p><p>&#8230;Hier w&#252;rde etwas Musik gespielt und ein Countdown eingeblendet werden, damit der interessierte Leser eine L&#246;sung erarbeiten kann <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> &#8230;</p><p>Meine L&#246;sung funktioniert, sofern die Symbole im Alphabet einem Symbol in UTF-8 entsprechen und ist trivial: Zuerst werden alle Permutationen gebildet und der RegExp daran getestet. Aus reinem Masochismus habe ich versucht, dies in Python 3.2 zu implementieren. Und nein, <em>itertools</em> packt das nicht!</p><p>Ein Testdurchgang k&#246;nnte folgendermassen aussehen (Das Alphabet besteht aus 0 und 1):</p><pre>
$ python3.2 msfr.py -v '(0*|1*)(0*|1*)(0*|1*)' 0 1
Σ: {0, 1}
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with ε MATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 0 MATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 1 MATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 00 MATCH
[...]
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 0011 MATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 0100 MATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 0101 NOMATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 0110 MATCH
[...]
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 1010 NOMATCH
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 1011 MATCH
[...]
  Testing: '^(0*|1*)(0*|1*)(0*|1*)$' with 1111 MATCH
Found nonmatching strings with length 4: 0101, 1010
</pre><p>Klappt also nicht schlecht. ε ist &#252;brigens der leere String <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p><p>Dies ist mein erstes Script in Python &#252;berhaupt. F&#252;r Anregungen und Verbesserungsvorschl&#228;ge bin ich mehr als dankbar:<br
/> [cc lang="python"]<br
/> #!/usr/bin/env python<br
/> &#8221;&#8217;<br
/> Find a minimal non-matching string for a given regular expression and an alphabet.<br
/> Created on Oct 1, 2011</p><p>@author: Skaldrom Y. Sarg<br
/> &#8221;&#8217;<br
/> import argparse<br
/> import re<br
/> import sys</p><p>def allstrings(alphabet, length):<br
/> &#8220;&#8221;"Find the list of all strings of &#8216;alphabet&#8217; of length &#8216;length&#8217;&#8221;"&#8221;</p><p> if length == 0:<br
/> return [""]<br
/> c = []<br
/> for i in range(length): # @UnusedVariable<br
/> c = [[x] + y for x in alphabet for y in c or [[]]]</p><p> return c</p><p>if __name__ == &#8216;__main__&#8217;:<br
/> scriptHelp= &#8220;msfr: Find a minimal non-matching string for a given regular expression and an alphabet.\n\<br
/> \n\<br
/> Example: python3 msfr.py &#8216;(0*|1*)(0*|1*)(0*|1*)&#8217; 0 1&#8243;</p><p> parser = argparse.ArgumentParser(description=scriptHelp, epilog=&#8217;Written by Michael Schneider.&#8217;)<br
/> parser.add_argument(&#8216;-v, &#8211;verbose&#8217;, dest=&#8217;verbose&#8217;, action=&#8217;store_true&#8217;, default=False, help=&#8217;output more verbose&#8217;)<br
/> parser.add_argument(&#8216;-m, &#8211;maxlength&#8217;, dest=&#8217;maxlength&#8217;, default=10, help=&#8217;max number of symbols to test (default: 10)&#8217;)<br
/> parser.add_argument(&#8216;regexp&#8217;, type=str, nargs=1, help=&#8217;the regular expression&#8217;)<br
/> parser.add_argument(&#8216;alphabet&#8217;, metavar=&#8217;S', type=str, nargs=&#8217;+', help=&#8217;a symbol of the alphabet&#8217;)<br
/> args = parser.parse_args()</p><p> # Start<br
/> if(args.verbose):<br
/> print(&#8216;Σ: {&#8216; + &#8220;, &#8220;.join(args.alphabet)+&#8217;}')</p><p> foundNonMatch = []<br
/> for wordlength in range(0, args.maxlength):<br
/> for string in allstrings(args.alphabet, wordlength):<br
/> testString = &#8220;&#8221;.join(string)<br
/> testRegexp = args.regexp[0]<br
/> if args.verbose:<br
/> print(&#8221;  Testing: &#8216;^&#8221; + testRegexp + &#8220;$&#8217; with &#8221; + (testString if len(testString) != 0 else &#8216;ε&#8217;), end=&#8221;")<br
/> try:<br
/> result = re.match(&#8216;^&#8217; + testRegexp + &#8216;$&#8217;, testString)<br
/> except re.error as exc:<br
/> if args.verbose:<br
/> print(&#8220;\n&#8221;)<br
/> print(&#8220;ERROR &#8211; Invalid regexp: &#8221; + str(exc))<br
/> sys.exit(0)</p><p> if(result != None):<br
/> if args.verbose:<br
/> print(&#8221; MATCH&#8221;)<br
/> else:<br
/> foundNonMatch.append(testString)<br
/> if args.verbose:<br
/> print(&#8221; NOMATCH&#8221;)<br
/> if(foundNonMatch):<br
/> break<br
/> if not foundNonMatch:<br
/> print(&#8220;No nonmatching strings found until length &#8221; + args.maxlength + &#8220;.&#8221;)<br
/> else:<br
/> foundStingsLength = str(len(foundNonMatch[0]))<br
/> foundStrings = &#8220;, &#8220;.join(foundNonMatch) if len(foundNonMatch[0]) > 0 else &#8216;ε&#8217;<br
/> print(&#8220;Found nonmatching string&#8221; + (&#8220;s&#8221; if len(foundNonMatch) else &#8220;&#8221;) + &#8221; with length &#8221; + foundStingsLength +&#8221;: &#8221; +foundStrings)<br
/> [/cc]</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2011/10/15/programmierquiz-kurzestes-nicht-passendes-wort-finden-auf-dass-ein-regexp-nicht-passt/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Dynamische Mailsignaturen in Apple 10.7 (Lion)</title><link>http://blog.oncode.info/2011/10/13/dynamische-mailsignaturen-in-apple-10-7-lion/</link> <comments>http://blog.oncode.info/2011/10/13/dynamische-mailsignaturen-in-apple-10-7-lion/#comments</comments> <pubDate>Thu, 13 Oct 2011 10:45:38 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Applikationen]]></category> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[apple]]></category> <category><![CDATA[applescript]]></category> <category><![CDATA[dynamische signatur]]></category> <category><![CDATA[lion shortcut]]></category> <category><![CDATA[mac]]></category> <category><![CDATA[mai.app]]></category> <category><![CDATA[tastaturkürzel]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1880</guid> <description><![CDATA[Auch auf dem Mac kann man dynamische Signaturen f&#252;r Mails benutzen.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2011/10/dynmail.png" alt="Dynamisches Mail" title="" width="172" height="136" class="lead" align="left" />Wie man in <a
href="/2008/07/28/dynamische-signatur-e-schrecking-fuer-foren-myspace/">verschiedenen</a>, <a
href="/2006/08/31/sich-andernde-mailsignaturen-mit-fortune/">schon geschriebenen Blogeintr&#228;gen</a> sehen kann, ist es mir ein Anliegen, die Welt mit etwas Dynamik zu versehen, zumindest was Mailsignaturen betrifft. Ja, auch in Mac OSX ist das m&#246;glich!</p><p>Von Haus aus bringt OSX keine M&#246;glichkeit mehr mit, dynamische Signaturen zu erstellen. Entweder, weil es so gedacht war, weil diese Funktion in die Cloud ausgelagert wurde <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> oder wegen <a
href="https://discussions.apple.com/message/6904290?messageID=6904290#6904290">eines Bugs</a>. Ich habe so ziemlich alles durchprobiert, was <a
href="http://hints.macworld.com/article.php?story=20040503211932363">dieser Artikel &#252;ber fortunes</a> und andere Websites beschrieben haben, doch leider l&#228;uft es nicht &#8211; oder nicht mehr &#8211; so.</p><p>Erkenntnisse:</p><ul><li>AppleScripts m&#246;chten nicht einfach so mit Tastaturk&#252;rzeln versehen werden.</li><li>Das <em>AppleScript Utility</em> gibt es nicht mehr. Um AppleScripts zu aktivieren muss der <em>AppleScript-Editor</em> ge&#246;ffnet werden und das Skriptmen&#252; in den Einstellungen aktiviert werden (siehe Abbildung).</li></ul><div
id="attachment_1889" class="wp-caption alignnone" style="width: 674px"><img
src="http://blog.oncode.info/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-10-um-18.08.01.png" alt="" title="Skript-Men&#252; im neuen AppleScript-Editor" width="664" height="454" class="size-full wp-image-1889" /><p
class="wp-caption-text">Skript-Men&#252; im neuen AppleScript-Editor</p></div><p>Will man doch mittels fortune dynamische Signaturen erzeugen, muss man wie Folgt vorgehen:</p><ol><li>Zuerst braucht man <em>fortune</em>. Dies kann man sich beispielsweise per <a
href="http://www.macports.org/">MacPorts</a> beschaffen. Damit MacPorts l&#228;uft, muss XCode vom AppStore installiert werden.</li><li>Dann sollte man sich eine Zitatsammlung bereit legen, wie im <a
href="/2006/08/31/sich-andernde-mailsignaturen-mit-fortune/">Blogeintrag zu dynamischen Signaturen</a> beschrieben.</li><li>Dann muss man den <em>Automator</em> starten und einen neuen Service erstellen.</li><li>Grundeinstellung: &#8220;Dienst empf&#228;ngt <b>keine Eingabe</b> in <b>Mail.app</b>&#8220;</li><li>Als Aktion <em>AppleScript ausf&#252;hren</em> suchen und einf&#252;gen.</li><li>Das AppleScript:<br
/> [cc lang="AppleScript"]<br
/> on run {input, parameters}</p><p> tell application &#8220;Mail&#8221;<br
/> activate<br
/> make new outgoing message with properties ¬<br
/> {content:do shell script &#8220;/Users/linux/Documents/sigs/psignature-mac&#8221;, visible:true}<br
/> end tell<br
/> return input<br
/> end run<br
/> [/cc]<br
/> Nat&#252;rlich muss der Pfad angepasst werden. <em>psignature-mac</em> ist ein Script mit folgendem Inhalt:<br
/> [cc lang="bash"]<br
/> #! /bin/bash<br
/> echo &#8221;  Viele Gr&#252;sse&#8221;<br
/> echo &#8221;     Skaldrom&#8221;<br
/> echo &#8220;-=-=-=-=-=-=-=-=-&#8221;<br
/> /opt/local/bin/fortune `dirname &#8220;$0&#8243;`/quotes/shorties<br
/> [/cc]<br
/> Dann alles speichern und einen Kaffee trinken oder Kekse backen gehen.</li><li>Um den Tastaturk&#252;rzel festzulegen, muss die <em>Systemeinstellungen</em> ge&#246;ffnet werden. Dort auf <em>Tastatur</em> &rarr; <em>Tastaturkurzbefehle</em> gehen. Der Dienst sollte irgendwo erscheinen und ein Klick auf den leeren Platz rechts davon erm&#246;glicht es, einen Tastaturkurzbefehl einzugeben.<br
/> <img
src="http://blog.oncode.info/wp-content/uploads/2011/10/keyboard.png" alt="" title="keyboard" width="639" height="313" class="alignnone size-full wp-image-1896" /></li><li>Mail &#246;ffnen, Tastaturkurzbefehl (ja, ich mag dieses Wort) dr&#252;cken und sich freuen.</li></ol><p>Es gibt auch eine L&#246;sung f&#252;r existierende Mails:<br
/> [cc lang="AppleScript"]<br
/> on run {input, parameters}</p><p> tell application &#8220;Mail&#8221;<br
/> activate<br
/> get do shell script &#8220;/Users/linux/Documents/sigs/psignature-mac&#8221;<br
/> copy return &#038; the result ¬<br
/> to theFortune</p><p> tell application &#8220;System Events&#8221;<br
/> tell process &#8220;Mail&#8221;<br
/> keystroke theFortune<br
/> end tell<br
/> end tell<br
/> end tell<br
/> return input<br
/> end run<br
/> [/cc]</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2011/10/13/dynamische-mailsignaturen-in-apple-10-7-lion/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Experimentelle Analysen von PHP Code f&#252;r Enthusiasten</title><link>http://blog.oncode.info/2011/03/07/experimentelle-analysen-von-php-code-fur-enthusiasten/</link> <comments>http://blog.oncode.info/2011/03/07/experimentelle-analysen-von-php-code-fur-enthusiasten/#comments</comments> <pubDate>Mon, 07 Mar 2011 13:57:48 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[Analyse]]></category> <category><![CDATA[Doxygen]]></category> <category><![CDATA[Jenkins]]></category> <category><![CDATA[JSLint]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[php-Lint]]></category> <category><![CDATA[phpcallgraph]]></category> <category><![CDATA[phuml]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1790</guid> <description><![CDATA[Weitergehende Tools zur Analyse von PHP-Code.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/experiment.png" alt="" title="experiment" height="120" class="lead" align="left" />W&#228;hrend ich im <a
href="/2011/03/02/php-code-analyse-mit-ant/">vorhergehenden Artikel</a> die &#8220;Standardtools&#8221; f&#252;r die Analyse von PHP verwendet habe, m&#246;chte ich hier einen Schritt weitergehen. Wenn ich schon meine Programme analysieren lasse, dann richtig <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ! Darum hier ein paar nicht-standard Analysetools f&#252;r PHP, die ich als eher experimentell bezeichnen w&#252;rde:</p><ul><li>doxygen</li><li>phuml</li><li>php-lint</li><li>PHP Callgraph</li></ul><h3>Doxygen statt phpDocumentor</h3><p>Ich hab gr&#246;ssten Respekt vor dem <a
href="http://www.phpdoc.org/">phpDocumentor</a> Projekt, aber <a
href="http://www.doxygen.org">Doxygen</a> hat ein paar Vorteile:</p><ul><li>Es kann mit nicht-UTF-8-Dateien umgehen.</li><li>Es ist schneller.</li><li>Es kommt mit anonymen Funktionen klar.</li></ul><p>Leider ist es nicht zu 100% kompatibel zu phpDocumentor. Es hat M&#252;he mit Kurzbeschreibungen, Parametern, etc.</p><p>Die Inbetriebnahme ist ganz einfach: mittels <tt>doxygen -g CONFIGNAME</tt> wird eine Standardkonfiguration erstellt, die man am Besten sofort editiert und mindestens folgende Dinge anpasst:</p><dl><dt>Name des Projekts</dt><dd><tt>PROJECT_NAME = Simple PHP Fixtures System</tt></dd><dt>Ausgabeverzeichnis</dt><dd><tt>OUTPUT_DIRECTORY       = statistics/html/doc/api/</tt></dd><dt>Auch private Members anzeigen</dt><dd><tt>EXTRACT_PRIVATE        = YES</tt></dd><dt>Verzeichnis des zu dokumentierenden Codes</dt><dd><tt>INPUT                  = ../code</tt></dd><dt>SVN-Verzeichnisse nicht durchsuchen</dt><dd><tt>EXCLUDE_PATTERNS       = */.svn/*</tt></dd><dt>Automatische Kurzbeschreibungen</dt><dd><tt>JAVADOC_AUTOBRIEF      = YES</tt></dd></dl><p>Wird der Dokumentationsvorgang nun mittels <tt>doxygen CONFIGNAME</tt> gestartet, so geht es unglaublich schnell und man hat eine peppige API-Dokumentation:</p><p><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/doxygen0.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/doxygen0-300x161.png" alt="" title="doxygen0" width="300" height="161" class="alignnone size-medium wp-image-1813" /></a><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/doxygen1.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/doxygen1-300x161.png" alt="" title="doxygen1" width="300" height="161" class="alignnone size-medium wp-image-1814" /></a></p><p>Das Ant-Target aus dem <a
href="/2011/03/02/php-code-analyse-mit-ant/">letzten Artikel</a> kann folgendermassen umgeschrieben werden:<br
/> [cc lang="xml"]<br
/> <target
name="doc-phpdoc"><br
/> <exec
executable="doxygen" failonerror="false" dir="${basedir}"><br
/> <arg
value="${doxygenconf}"/><br
/> </exec><br
/> </target><br
/> [/cc]</p><p><tt>doxygenconf</tt> muss nat&#252;rlich noch in der properties-Datei mit dem Pfad der Configdatei gef&#252;llt werden.</p><h3>Phuml</h3><p><a
href="http://westhoffswelt.de/projects/phuml.html">phUML</a> erzeugt &#8211; nicht ganz standardkonforme &#8211; UML-Diagramme aus PHP-Code. Je nach Veranlagung sieht man ziemlich schnell, wenn etwas in der Struktur schief gelaufen ist. Wurde das Phuml-SVN ausgecheckt, kann mit dem Kommando <tt>php phuml -r <PFAD_ZUM_CODE> -graphviz -createAssociations true -neato uml.png</tt> ein Diagramm erstellt werden. M&#246;chte man lieber ein anderes Format als ng, so kann das in der Datei <tt>phuml/src/classes/processor/neato.php</tt>, Funktion <tt>execute()</tt> auf der Zeile mit <tt>'neato -Tpng -o [...]</tt> umgestellt werden. Zur Verf&#252;gung stehen viele: ps, pdf, svg, gif, &#8230; <tt>man neato</tt> gibt gerne Auskunft.</p><div
id="attachment_1817" class="wp-caption alignnone" style="width: 310px"><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/uml.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/uml-300x285.png" alt="" title="uml" width="300" height="285" class="size-medium wp-image-1817" /></a><p
class="wp-caption-text">UML-Diagramm</p></div><h3>PHP-Lint</h3><p>Die <a
href="http://de.wikipedia.org/wiki/Lint_%28Programmierwerkzeug%29">Lints</a> haben eine lange Geschichte in der Programmierung, sie sind sozusagen <em>das</em> Werkzeug f&#252;r statische Code-Analyse. Im Gegensatz zu einigen Anderen bin ich der Meinung, dass <tt>php -l</tt> ein Syntaxcheck und damit <em>kein</em> Lint ist. F&#252;r PHP heisst das Teil <a
href="http://www.icosaedro.it/phplint/">PHPLint</a> und ist ebenso kritisch wie die Lints f&#252;r andere Sprachen. Es macht Gebrauch von PHP-Doc Kommentaren f&#252;r erweiterte Analyseinformationen. Leider kann PHP-Lint (im Moment) nicht mit Closures umgehen und etwas speziell ist, dass die verwendeten <a
href="http://www.icosaedro.it/phplint/tutorial.html">PHP-Module</a> zu Beginn der zu untersuchenden Dateien aufgef&#252;hrt werden m&#252;ssen:<br
/> [cc lang="php"]<br
/> /*.<br
/> require_module &#8216;standard&#8217;;<br
/> require_module &#8216;pdo&#8217;;<br
/> require_module &#8216;session&#8217;;<br
/> .*/<br
/> [/cc]</p><p>Ebenfalls kann man ihm mit expliziten castings wie <tt>(string)</tt> oder etwas h&#228;sslichen Kommentaren <tt>/*. string .*/</tt> im Code bei der Arbeit unterst&#252;tzen.</p><p>Die Installation l&#228;uft folgendermassen:</p><ol><li><a
href="http://www.icosaedro.it/phplint/download.html">Download</a> der entsprechenden Version (pure-c f&#252;r Linux).</li><li>Entpacken des Tars und eventuell kompilieren.</li><li>Ein eigenes Verzeichnis erstellen (bspw. <tt>/opt/php/phplint</tt></li><li>Das Verzeichnis <tt>modules</tt> und die Datei <tt>src/phplint</tt> in das erstellte Verzeichnis kopieren.</li><li>phplint starten mittels: <tt>/opt/php/phplint/phplint --modules-path /opt/php/phplint/modules/ <PFAD-ZUR-STARTDATEI-DES-CODES></tt></li><li> Nun sollten ganz viele Fehler ausgegeben werden.<br
/> [cc]<br
/> &#8230;<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:136: Warning: comparing (mixed) == (mixed): `mixed&#8217; type cannot be compared. Hint: check and convert mixed values to the appropriate type, or consider to use strict comparison operators === or !== if it is the case.<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:139: notice: &#8216;continue EXPR&#8217;: unadvised programming practice<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:135: notice: variable `$foreignkey&#8217; assigned but never used<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:126: notice: variable `$candidateData&#8217; assigned but never used<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:170: Warning: comparing (mixed) == (string): `mixed&#8217; type cannot be compared. Hint: check and convert mixed values to the appropriate type, or consider to use strict comparison operators === or !== if it is the case.<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:176: Warning: applying the `[]&#8216; operator to array of undefined index type<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:178: ERROR: method `SimpleFixtures::_untangleTree()&#8217;: expected return type void, found expression of type array[]string<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:183: Warning: calling `SimpleFixtures::_untangleTree()&#8217; declared in line 160, argument no. 1: found type `mixed&#8217;, required type `string&#8217;<br
/> /home/skaldrom/svn/syncic/SimplePHPFixturesSystem/trunk/code/SimpleFixtures.php:188: Warning: applying the `[]&#8216; operator to array of undefined index type<br
/> &#8230;<br
/> [/cc]</li></ol><p>Damit phplint auch in <a
href="http://jenkins-ci.org/">Jenkins</a> genutzt werden kann, habe ich einen kleinen Wrapper geschrieben, der die Ausgabe in JSLint-XML umwandelt. Damit kann diese Ausgabe komfortabel beim Violations-Modul eingebaut werden. Die PHP-Datei muss im selben Verzeichnis wie phplint platziert werden und geht davon aus, dass <tt>modules</tt> ein Unterverzeichnis ist. Download hier: <a
href='http://blog.oncode.info/wp-content/uploads/2011/03/phplint.php_.zip'>phplint.php</a>.</p><div
id="attachment_1825" class="wp-caption alignnone" style="width: 310px"><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/phplint-jenkins.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/phplint-jenkins-300x151.png" alt="" title="phplint-jenkins" width="300" height="151" class="size-medium wp-image-1825" /></a><p
class="wp-caption-text">PHPLint in Jenkins</p></div><p>Ein grosses Problem gibt es in Netbeans&#8230; Wird der PHP-Code neu formatiert, werden bei den Kommentaren um die Module herum Leerschl&#228;ge platziert und phplint erkennt sie nimmer. Sehr nervig, echt&#8230;</p><h3>PHP Call Graph</h3><p><a
href="http://phpcallgraph.sourceforge.net/">php CallGraph</a> ist ein wunderbares St&#252;ck Software, dass einige bestehende Libraries clever vereint. Es produziert Aufrufgraphen einer bestehenden Software, die ziemlich effizient mit dem intuitiven Plan des Entwicklers verglichen werden k&#246;nnen. Sch&#246;ne <a
href="http://phpcallgraph.sourceforge.net/examples/">Beispiele</a> gibt es auf der Website.</p><p>phpCallGraph verl&#228;sst sich unter Anderem auf phpDoc-Kommentare. Die sollten also entweder gar nicht oder einigermassen gescheit ausgef&#252;llt werden. Sollte es trotzdem ein Problem geben, so kann zum Debuggen die Zeile 517 und 518 in der Datei <tt>lib/instantsvc/components/CodeAnalyzer/src/code_analyzer.php</tt> entkommentiert werden:<br
/> [cc lang="php"]<br
/> echo &#8216;$filename = &#8216;, var_export($filename, true), &#8220;;\n&#8221;;<br
/> echo &#8216;$result   = &#8216;, var_export($result, true), &#8220;;\n&#8221;;<br
/> [/cc]</p><p>Ich habe einen Patch eingereicht, der einige Warnings entfernt und eine &#8220;ignore&#8221; Option hinzuf&#252;gt, der alsbald angewendet wird.<br
/> <embed
src="/wp-content/uploads/2011/03/callgraph.svg" type="image/svg+xml"></embed></p><h3>Da war ich zu bl&#246;d daf&#252;r</h3><p>F&#252;r <a
href="http://trac2.assembla.com/php-ast">php-ast</a> und <a
href="http://www.program-transformation.org/PHP/PhpSatReleases">php-sat</a> war ich leider zu bl&#246;d. Ich hatte weder Nerven noch Zeit f&#252;r die Build-Prozedur und die Projekte scheinen auch seit l&#228;ngerem still zu liegen. Die Ideen w&#228;ren aber grunds&#228;tzlich interessant.</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2011/03/07/experimentelle-analysen-von-php-code-fur-enthusiasten/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <series:name><![CDATA[Continuous Integration]]></series:name> </item> <item><title>PHP Code-Analyse mit Ant</title><link>http://blog.oncode.info/2011/03/02/php-code-analyse-mit-ant/</link> <comments>http://blog.oncode.info/2011/03/02/php-code-analyse-mit-ant/#comments</comments> <pubDate>Wed, 02 Mar 2011 07:52:41 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[ant]]></category> <category><![CDATA[code analyse]]></category> <category><![CDATA[Coding]]></category> <category><![CDATA[Hudson]]></category> <category><![CDATA[Jenkins]]></category> <category><![CDATA[PEAR]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[Statistik]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1772</guid> <description><![CDATA[Mittels ant werden hier einige Statistiken zu bestehenden PHP-Programmen erstellt.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/graph.png" alt="Graph Lead" title="graph" width="172" height="136" class="lead" align="left" />Ich mag Code. Nicht jeden nat&#252;rlich, &#8220;Douchebag&#8221; Code nervt, Code der aussieht wie ein schreiender Quasimodo oder Code der von einem Dr. Frankenstein in Ausbildung erstellt wurde sind bemitleidenswert und sehr viel Code stinkt. Code, der gef&#228;llt (wie beispielsweise der von <a
href="http://www.frigidor.ch">Frigidor</a>) ist clever ohne zu bluffen, kurz aber nicht kryptisch, tut etwas, ist lesbar und hat einige Wows drin.</p><p>Diese Bewunderung sollte in Zahlen gefasst werden: mit Statistiken, Diagrammen, Balken und Graphen. Dies dient zur Vorbereitung der &#8220;Continuous Integration&#8221; mit Jenkins/Hudson, die in dieser Serie behandelt wird. Als Quellen haben vor allem <a
href="http://jenkins-php.org/">jenkins-php</a> und ein &#228;usserst Lesbarer Artikel im <a
href="http://entwickler-magazin.de/zonen/magazine/psecom,id,17,ausgabe,428,p,0.html">Entwickler Magazin 01/11</a> gedient.</p><p>Hier wird nun gezeigt, wie mit einem Befehl:</p><ul><li>Die Unittests durchf&#252;hrt.</li><li>Qualitative Softwaremetriken mit <a
href="http://pdepend.org/">PHP Depend</a> misst: Hierarchietiefe, Komplexit&#228;t, &#8230;</li><li>Unsch&#246;ne Teile mit <a
href="http://phpmd.org/">PHPMD</a> identifiziert.</li><li>Copy-Paste-Verbrecher aufsp&#252;hrt mit <a
href="https://github.com/sebastianbergmann/phpcpd">phpcpd</a>.</li><li>Den Coding-Style pr&#252;ft mit dem <a
href="http://pear.php.net/package/PHP_CodeSniffer/redirected">PHP Code Sniffer</a>.</li><li>Quantitative Softwaremetriken mit <a
href="https://github.com/sebastianbergmann/phploc">phploc</a> misst.<li><li>Die <a
href="http://www.phpdoc.org/">PHPDoc</a>-Doku erstellt.</li><li>Die Resultate mit dem <a
href="https://github.com/mayflowergmbh/PHP_CodeBrowser">PHP Code Browser</a> sch&#246;n darstellt.</li></ul><h3>PEAR Tools installieren</h3><p><a
href="http://pear.php.net/">PEAR</a> stellt viele Hilfsprogramme zur Analyse zur Verf&#252;gung. Darum muss zuerst pear und danach diese Tools installiert werden. Am Besten als Root, wenn man nicht ein riesen Chaos verursachen m&#246;chte:<br
/> [cc lang="bash"]<br
/> sudo pear upgrade PEAR<br
/> sudo pear channel-discover pear.pdepend.org<br
/> sudo pear channel-discover pear.phpmd.org<br
/> sudo pear channel-discover pear.phpunit.de<br
/> sudo pear channel-discover components.ez.no<br
/> sudo pear channel-discover pear.symfony-project.com</p><p>sudo pear install Text_Highlighter-0.7.1<br
/> sudo pear install pdepend/PHP_Depend<br
/> sudo pear install phpmd/PHP_PMD<br
/> sudo pear install phpunit/phpcpd<br
/> sudo pear install phpunit/phploc<br
/> sudo pear install PHPDocumentor<br
/> sudo pear install PHP_CodeSniffer<br
/> sudo pear install &#8211;alldeps phpunit/PHP_CodeBrowser<br
/> sudo pear install &#8211;alldeps phpunit/PHPUnit<br
/> [/cc]</p><h3>Ant build Files</h3><p>Um den Build zu steuern, eignet sich <a
href="http://ant.apache.org/">Ant</a> sehr gut. Zwar ist es XML (Anwender sollen kein XML schreiben m&#252;ssen!), stellt aber ein paar vern&#252;nftige Funktionen bereit. F&#252;r einen Build braucht es eine Properties- und eine XML-Datei, die am Besten in ein eigenes <tt>build</tt> Verzeichnis verfrachtet werden. In der Propertiesdatei k&#246;nnen Variablen gesetzt und eventuell auf verschiedene Umgebungen reagiert werden. Bei mir heisst sie <tt>build.properties</tt> und sieht ziemlich einfach aus:<br
/> [cc lang="bash"]<br
/> # Source directory<br
/> source=${basedir}/../htdocs/code<br
/> # Target directory<br
/> builddir=${basedir}/statistics<br
/> # Where are the Unittests<br
/> unittests=${basedir}/tests<br
/> testsuite=suite.php<br
/> # 10: Highest priority, 1: Lowest<br
/> niceness=1<br
/> # Set to a random String if you have no external libraries. PHPCPD accepts only ONE directory<br
/> extlib=inc/external<br
/> phpdocextlib=*${extlib}/*<br
/> extlibs=${extlib},\\.svn<br
/> # Files to consider<br
/> suffixes=php<br
/> # Ruleset for Code Sniffer<br
/> csruleset=${basedir}/conf/syncic<br
/> [/cc]</p><p>Hier sieht man auch schon eines der Hauptprobleme: Die Tools verwenden unterschiedliche Angaben um Verzeichnisse auszuschliessen: Pfade, RegExps, durch Komma getrennt, &#8230; Ziemlich m&#252;hsam.</p><p>Die Build-Datei <tt>build.xml</tt> ist etwas lange, aber gut verst&#228;ndlich. Zuerst werden die abstrakten Ziele eingerichtet und dann gibt es f&#252;r jedes Tool ein Target.</p><p>[cc lang="xml"]<br
/> <?xml version="1.0" encoding="UTF-8" ?></p> <project
name="Easy-Form" basedir="." default="build"> <property
file="build.properties"/> <nice
newpriority="${niceness}"/></p><p> <target
name="build" depends="test, doc" /></p><p> <target
name="clean"><br
/> <delete
dir="${builddir}" /><br
/> </target></p><p> <target
name="prepare" depends="clean"><br
/> <mkdir
dir="${builddir}/logs" /><br
/> <mkdir
dir="${builddir}/logs/coverage" /><br
/> <mkdir
dir="${builddir}/html" /><br
/> <mkdir
dir="${builddir}/html/jdepend" /><br
/> <mkdir
dir="${builddir}/html/unittests" /><br
/> <mkdir
dir="${builddir}/html/experimental" /><br
/> <mkdir
dir="${builddir}/html/doc" /><br
/> <mkdir
dir="${builddir}/html/doc/api" /><br
/> <mkdir
dir="${builddir}/html/doc/codebrowser" /><br
/> </target></p><p> <target
name="test" depends="test-unit, test-static" /></p><p> <target
name="test-static" depends="prepare"></p> <parallel
threadCount="2"> <sequential><br
/> <antcall
target="test-static-jdepend"/><br
/> <antcall
target="test-static-pmd"/><br
/> </sequential><br
/> <antcall
target="test-static-cpd"/><br
/> <antcall
target="test-static-checkstyle"/><br
/> <antcall
target="test-static-phploc"/> </parallel> </target></p><p> <target
name="doc" depends="prepare"><br
/> <sequential><br
/> <antcall
target="doc-phpdoc"/><br
/> <antcall
target="doc-phpcb"/><br
/> </sequential><br
/> </target></p><p> <br
/> <target
name="test-unit" depends="prepare"><br
/> <exec
executable="phpunit" failonerror="true" dir="${unittests}"><br
/> <arg
value="--log-junit" /><br
/> <arg
value="${builddir}/logs/junit.xml" /><br
/> <arg
value="--coverage-clover" /><br
/> <arg
value="${builddir}/logs/coverage/clover.xml" /><br
/> <arg
value="--coverage-html" /><br
/> <arg
value="${builddir}/logs/coverage" /><br
/> <arg
value="--testdox-html" /><br
/> <arg
value="${builddir}/html/unittests/index.html" /><br
/> <arg
value="--configuration" /><br
/> <arg
value="${phpunitconf}" /><br
/> <arg
value="${testsuite}" /><br
/> </exec><br
/> </target></p><p> <br
/> <target
name="test-static-jdepend"><br
/> <exec
executable="pdepend" failonerror="false" dir="${source}"><br
/> <arg
value="--jdepend-xml=${builddir}/logs/jdepend.xml" /><br
/> <arg
value="--jdepend-chart=${builddir}/html/jdepend/dependencies.svg" /><br
/> <arg
value="--overview-pyramid=${builddir}/html/jdepend/pyramid-overview.svg" /><br
/> <arg
value="--summary-xml=${builddir}/logs/jdepend-summary.xml" /><br
/> <arg
value="--phpunit-xml=${builddir}/logs/jdepend-phpunit.xml" /><br
/> <arg
value="--ignore=${extlibs}" /><br
/> <arg
value="--suffix=${suffixes}" /><br
/> <arg
value="${source}" /><br
/> </exec><br
/> </target></p><p> <target
name="test-static-pmd"><br
/> <exec
executable="phpmd" failonerror="false" dir="${source}"><br
/> <arg
value="${source}" /><br
/> <arg
value="xml" /><br
/> <arg
value="codesize,design,naming,unusedcode" /><br
/> <arg
value="--reportfile" /><br
/> <arg
value="${builddir}/logs/pmd.xml" /><br
/> <arg
value="--suffixes" /><br
/> <arg
value="${suffixes}" /><br
/> <arg
value="--exclude" /><br
/> <arg
value="${extlibs}" /><br
/> </exec><br
/> </target></p><p> <target
name="test-static-cpd"><br
/> <exec
executable="phpcpd" failonerror="false" dir="${source}"><br
/> <arg
value="--log-pmd" /><br
/> <arg
value="${builddir}/logs/cpd.xml" /><br
/> <arg
value="--suffixes" /><br
/> <arg
value="${suffixes}" /><br
/> <arg
value="--exclude" /><br
/> <arg
value="${extlib}" /><br
/> <arg
value="${source}" /><br
/> </exec><br
/> </target></p><p> <target
name="test-static-checkstyle"><br
/> <exec
executable="phpcs" failonerror="false" dir="${source}"><br
/> <arg
value="--report=checkstyle" /><br
/> <arg
value="--report-file=${builddir}/logs/checkstyle-result.xml" /><br
/> <arg
value="--extensions=${suffixes}" /><br
/> <arg
value="--ignore=${extlibs}" /><br
/> <arg
value="--standard=${csruleset}" /><br
/> <arg
value="${source}" /><br
/> </exec><br
/> </target></p><p> <target
name="test-static-phploc"><br
/> <exec
executable="phploc" failonerror="false"><br
/> <arg
value="--log-csv" /><br
/> <arg
value="${builddir}/logs/loc.csv" /><br
/> <arg
value="--suffixes" /><br
/> <arg
value="php" /><br
/> <arg
value="--exclude" /><br
/> <arg
value="${extlib}" /><br
/> <arg
value="${source}" /><br
/> </exec><br
/> </target></p><p> <br
/> <target
name="doc-phpdoc"><br
/> <exec
executable="phpdoc" failonerror="false" dir="${source}"><br
/> <arg
value="--directory"/><br
/> <arg
value="${source}" /><br
/> <arg
value="--ignore" /><br
/> <arg
value="${phpdocextlib}" /><br
/> <arg
value="--target" /><br
/> <arg
value="${builddir}/html/doc/api" /><br
/> <arg
value="--output" /><br
/> <arg
value="HTML:frames:earthli" /><br
/> <arg
value="--title" /><br
/> <arg
value="${ant.project.name}" /><br
/> </exec><br
/> </target></p><p> <target
name="doc-phpcb"><br
/> <exec
executable="phpcb" failonerror="false"><br
/> <arg
value="--log"/><br
/> <arg
value="${builddir}/logs" /><br
/> <arg
value="-i" /><br
/> <arg
value="${extlibs}" /><br
/> <arg
value="--output" /><br
/> <arg
value="${builddir}/html/doc/codebrowser" /><br
/> </exec><br
/> </target> </project> [/cc]</p><p>Ist alles richtig eingerichtet, kann der Build mit dem Kommando <tt>ant</tt> gestartet werden.</p><p><b>Hinweis:</b> Das Coverage-XML und das Coverage-HTML m&#252;ssen im gleichen Verzeichnis sein. Wenn der Clover-Report nicht funktioniert und ein 404 not found gibt, liegt es wahrscheinlich daran&#8230;</p><h3>Code Sniffer einrichten</h3><p>Der Stil des Codes (Einr&#252;cken, Benamsung, &#8230;) ist sehr pers&#246;nlich. Sehrwahrscheinlich werden viele Warnungen gezeigt, mit denen man gar nicht einverstanden ist. Das ist aber kein Problem, denn der Codesniffer erlaubt es, eigene Codingstandards zu definieren. mit der neuen Version 1.3 (die mittels <tt>sudo pear install PHP_CodeSniffer-1.3.0RC2</tt> installiert werden kann) geht das ziemlich einfach. Daf&#252;r brauch es in einem Grundverzeichnis eine <tt>ruleset.xml</tt> Datei und in einem Unterverzeichnis <tt>Sniff</tt> die eigenen Regeln, in PHP auscodiert. In <tt>ruleset.xml</tt> kann man sich frei an nestehenden Regeln bedienen. Meines sieht folgendermassen aus:<br
/> [cc lang="xml"]<br
/> <?xml version="1.0"?><br
/> <ruleset
name="Syncic Standard"><br
/> <rule
ref="PEAR"><br
/> <exclude
name="Generic.WhiteSpace.DisallowTabIndent"/><br
/> <exclude
name="PEAR.ControlStructures.ControlSignature"/><br
/> </rule></p><p><rule
ref="Generic.VersionControl.SubversionProperties"/><br
/> <rule
ref="Generic.Classes.DuplicateClassName"/><br
/> <rule
ref="Generic.Strings.UnnecessaryStringConcat"/><br
/> <rule
ref="Generic.PHP.DeprecatedFunctions"/><br
/> <rule
ref="Generic.PHP.ForbiddenFunctions"/><br
/> <rule
ref="Generic.PHP.NoSilencedErrors"/></p><p><rule
ref="PEAR.WhiteSpace.ScopeIndent"></p> <properties> <property
name="indent" value="2"/> </properties> </rule><br
/> </ruleset><br
/> [/cc]</p><p>In dieser Datei &#252;bernehme ich vor allem den PEAR-Standard, konfiguriere etwas herum und mische ein paar allgemeine Regeln hinzu. Vorallem die Indentation mit Spaces wollte ich anpassen. Im <tt>Sniffs</tt> Verzeichnis habe ich meine eigenen Regeln. Beispielsweise <tt>ControlStructures/ControlSignatureSniff.php</tt> (angepasst vom PEAR Coding Standard:<br
/> [cc lang="php"]<br
/> <?php<br
/> /**<br
/> * Verifies that control statements conform to their coding standards.<br
/> */</p><p>if (class_exists(&#8216;PHP_CodeSniffer_Standards_AbstractPatternSniff&#8217;, true) === false) {<br
/> throw new PHP_CodeSniffer_Exception(&#8216;Class PHP_CodeSniffer_Standards_AbstractPatternSniff not found&#8217;);<br
/> }</p><p>/**<br
/> * Verifies that control statements conform to their coding standards.<br
/> */<br
/> class syncic_Sniffs_ControlStructures_ControlSignatureSniff extends PHP_CodeSniffer_Standards_AbstractPatternSniff {<br
/> /**<br
/> * Constructs a PEAR_Sniffs_ControlStructures_ControlSignatureSniff.<br
/> */<br
/> public function __construct() {<br
/> parent::__construct(true);</p><p> }//end __construct()</p><p> /**<br
/> * Returns the patterns that this test wishes to verify.<br
/> *<br
/> * @return array(string)<br
/> */<br
/> protected function getPatterns() {<br
/> return array(<br
/> &#8216;do {EOL&#8230;} while (&#8230;);EOL&#8217;,<br
/> &#8216;while(&#8230;) {EOL&#8217;,<br
/> &#8216;for(&#8230;) {EOL&#8217;,<br
/> &#8216;if(&#8230;) {EOL&#8217;,<br
/> &#8216;foreach(&#8230;) {EOL&#8217;,<br
/> &#8216;} else if(&#8230;) {EOL&#8217;,<br
/> &#8216;} elseif(&#8230;) {EOL&#8217;,<br
/> &#8216;} else {EOL&#8217;,<br
/> &#8216;do {EOL&#8217;,<br
/> );</p><p> }//end getPatterns()<br
/> }//end class<br
/> ?><br
/> [/cc]</p><h3>Was gewinnen wir damit?</h3><p>Nach einem <tt>ant</tt> haben wir zum Einen ganz viele XML-Dateien. In einem sp&#228;teren Beitrag werde ich zeigen, wie diese in ein &#8220;Continuous Integration&#8221; System integriert werden k&#246;nnen. F&#252;r Menscen Lesbar haben wir Folgendes:<br
/> Dokumentation unter <tt>statistics/html/doc/api/</tt>:<br
/><div
id="attachment_1797" class="wp-caption alignnone" style="width: 310px"><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/phpdoc.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/phpdoc-300x166.png" alt="" title="phpdoc" width="300" height="166" class="size-medium wp-image-1797" /></a><p
class="wp-caption-text">PHPDoc Beispiel</p></div><br
/> Eine Liste aller Style-Verst&#246;sse und Chaosattacken unter <tt>statistics/html/doc/codebrowser/</tt>:<br
/><div
id="attachment_1798" class="wp-caption alignnone" style="width: 310px"><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/codebrowser.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/codebrowser-300x166.png" alt="" title="codebrowser" width="300" height="166" class="size-medium wp-image-1798" /></a><p
class="wp-caption-text">Codebrowser Output</p></div><br
/> Ein Coverage-Report der Unittests unter <tt>statistics/html/unittests/coverage/</tt>:<br
/><div
id="attachment_1799" class="wp-caption alignnone" style="width: 310px"><a
href="http://blog.oncode.info/wp-content/uploads/2011/03/coverage.png"><img
src="http://blog.oncode.info/wp-content/uploads/2011/03/coverage-300x127.png" alt="" title="coverage" width="300" height="127" class="size-medium wp-image-1799" /></a><p
class="wp-caption-text">Coverage Report</p></div><br
/> Zwei nette Diagramme von PHP-Depend (eine <a
href="http://pdepend.org/documentation/handbook/reports/abstraction-instability-chart.html">Abstraction Instability Chart</a> und eine <a
href="http://pdepend.org/documentation/handbook/reports/overview-pyramid.html">Overview Pyramid</a>) unter <tt>statistics/html/jdepend</tt>:<br
/> <embed
height="300" src="http://blog.oncode.info/wp-content/uploads/2011/03/pyramid-overview.svg"" type="image/svg+xml" width="500"></embed><br
/> <embed
height="300" src="http://blog.oncode.info/wp-content/uploads/2011/03/dependencies.svg" type="image/svg+xml" width="500"></embed></p><h3>Fazit</h3><p>In einem n&#228;chsten Beitrag werde ich noch weitere Tools vorstellen und dann soll das alles ganz automatisch bei jeder &#196;nderung geschehen&#8230; Stay tuned&#8230;</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2011/03/02/php-code-analyse-mit-ant/feed/</wfw:commentRss> <slash:comments>3</slash:comments> <series:name><![CDATA[Continuous Integration]]></series:name> </item> <item><title>Programmierwettbewerbe: Zeit sollte man haben</title><link>http://blog.oncode.info/2010/10/28/programmierwettbewerbe-zeit-sollte-man-haben/</link> <comments>http://blog.oncode.info/2010/10/28/programmierwettbewerbe-zeit-sollte-man-haben/#comments</comments> <pubDate>Thu, 28 Oct 2010 08:07:43 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[Programmieren]]></category> <category><![CDATA[Programmierspiele]]></category> <category><![CDATA[Wettbewerb]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1539</guid> <description><![CDATA[Programmierwettbewerbe gibts so viele wie schon lange nicht mehr. Leider klemmt der vom Linux-Magazin irgendwie.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2010/10/thinking.png" alt="thinking" title="thinking" width="129" height="162" class="lead" align="left" />Leider kann ich mangels Zeit beim <a
href="/2010/09/06/jetzt-macht-auch-google-noch-ein-programmierwettbewerb/">Google AI-Contest</a> nicht mitmachen <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> . Und ich habe mich schon so gefreut. Naja, ich hoffe, es gibt einen N&#228;chsten.</p><p>Sehr interessant ist der Verlauf des <a
href="/2010/08/10/ein-programmierwettbewerb-vom-linux-magazin/">Wettbewerbs des Linux-Magazins</a>. Alles ging gut bis zum Abgabetermin am 12. September 2010. Ab dann war der Wurm drin: Die versprochenen Best&#228;tigungsmails an die Teilnehmer sind nie eingetroffen und der Organisator ist &#8220;abgetaucht&#8221;. Die Wettbewerbsteilnehmer haben viel Geduld gezeigt, das <a
href="http://wettbewerb.linux-magazin.de/">Wiki</a> regelm&#228;ssig von Spam gereinigt und abgewartet.</p><p>Nachdem die ersten unangenehmen Fragen aufgetaucht sind, hat sich die Organisation gemeldet und verk&#252;ndet, dass ein Bericht in der Novemberausgabe des Magazins erscheinen wird. Das ist er dann auch, aber mehr ist bis jetzt nicht passiert. Der Unmut wird immer gr&#246;sser, vor allem nachdem eine Teilnehmerliste ver&#246;ffentlicht wurde, auf der einige Bots fehlen. Leider gibt es weiterhin nur sp&#228;rlich Neuigkeiten. Eine Community ist ein Dialog: Wird er vom Organisator nicht gef&#252;hrt, f&#252;hrt ihn die Community alleine.</p><p>Wir sind hier organisatorisch mit einem &#228;hnlichen Problem konfrontiert: Programme in vielen Programmiersprachen werden abgegeben und m&#252;ssen von uns bewertet werden. Auch ein kleiner Mehraufwand pro einzelner L&#246;sung summiert sich schlussendlich zu sehr viel Zeit. Das Einzige, das hilft, ist Denken im <em>Voraus</em>. Klare Richtlinien und Regeln, sonst wird der Aufwand un&#252;berschaubar riesig. Es gibt Beispiele von erfolgreich durchgef&#252;hrten Wettbewerben: <a
href="/2008/08/23/avaloqix-unterhaltsamer-java-programmierwettbewerb/ ">Avaloqix</a> wurde life durchgef&#252;hrt mit Anwesenden, der <a
href="http://ai-contest.com/">Google AI-Contest</a> oder die alten Codezone (Microsoft) Wettbewerbe haben ebenfalls gezeigt, wie es gehen k&#246;nnte. Ich m&#246;chte den Organisatoren vom Linux-Magazin nicht in die Suppe spucken, und wenn sie die Durchf&#252;hrung noch hinkriegen, haben sie meinen gr&#246;ssten Respekt und viel gearbeitet, aber irgendwie war es ein Schnellschuss.</p><p>So ganz per Zufall habe ich noch eine nette Sache entdeckt: Das Freie Magazin f&#252;hrt ebenfalls einen <a
href="http://www.freiesmagazin.de/dritter_programmierwettbewerb">sehr interessanten Wettbewerb</a> durch. Tip: Das <tt>LIESMICH</tt> im Download lesen <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> &#8230;.</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2010/10/28/programmierwettbewerbe-zeit-sollte-man-haben/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <series:name><![CDATA[Linux-Magazin Wettbewerb]]></series:name> </item> <item><title>Jetzt macht auch Google noch ein Programmierwettbewerb</title><link>http://blog.oncode.info/2010/09/06/jetzt-macht-auch-google-noch-ein-programmierwettbewerb/</link> <comments>http://blog.oncode.info/2010/09/06/jetzt-macht-auch-google-noch-ein-programmierwettbewerb/#comments</comments> <pubDate>Mon, 06 Sep 2010 17:13:19 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Applikationen]]></category> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[AI]]></category> <category><![CDATA[Contest]]></category> <category><![CDATA[Google]]></category> <category><![CDATA[Programmierspiele]]></category> <category><![CDATA[Spiel]]></category> <category><![CDATA[Wettbewerb]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1528</guid> <description><![CDATA[Nun macht auch Google noch ein Programmierwettbewerb, und so wie es aussieht auch noch einen Spannenden...]]></description> <content:encoded><![CDATA[<p>Ich habe mich schon gefreut: Der W&#252;rfler zum <a
href="/2010/08/10/ein-programmierwettbewerb-vom-linux-magazin/">Linux-Magazin-Wettbewerb</a> ist fertiggestellt und eigentlich bereit zum Einsenden. Etwas entt&#228;uscht habe ich festgestellt, dass das Problem akademisch gel&#246;st und der optimale Spieler bekannt ist. Nun, der Thrill der noch bleibt ist, dass bei nur 100 Spielen auf 50 Punkte der Zufall eine seeeehr grosse Rolle spielt.</p><p>Doch zu fr&#252;h gefreut, jetzt kommt das: Die Google <a
href="http://www.ai-contest.com/">AI-Challenge</a>. Man kann nicht mal was gewinnen, aber das Spiel ist unglaublich genial: Es geht darum, Planeten zu erobern:</p><p><object
width="500" height="405"><param
name="movie" value="http://www.youtube.com/v/O0uxXZY-t-s?fs=1&amp;hl=en_US&amp;rel=0&amp;border=1"></param><param
name="allowFullScreen" value="true"></param><param
name="allowscriptaccess" value="always"></param><embed
src="http://www.youtube.com/v/O0uxXZY-t-s?fs=1&amp;hl=en_US&amp;rel=0&amp;border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="500" height="405"></embed></object></p><p>Es gibt jetzt schon <a
href="http://www.ai-contest.com/starter_packages.php">Starter-Packages f&#252;r verschiedene Sprachen</a> und das System ist ziemlich offen (f&#252;r unsere Shell-Krieger <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). Ab dem 10.09.2010 kann man sich dann offiziell eintragen und losranken.</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2010/09/06/jetzt-macht-auch-google-noch-ein-programmierwettbewerb/feed/</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>Ein Programmierwettbewerb vom Linux Magazin</title><link>http://blog.oncode.info/2010/08/10/ein-programmierwettbewerb-vom-linux-magazin/</link> <comments>http://blog.oncode.info/2010/08/10/ein-programmierwettbewerb-vom-linux-magazin/#comments</comments> <pubDate>Tue, 10 Aug 2010 16:53:11 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Applikationen]]></category> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[Game]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[Spiel]]></category> <category><![CDATA[Wettbewerb]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1503</guid> <description><![CDATA[Ein PHP-Ger&#252;st f&#252;r den Linux-Magazin Programmierwettbewerb.]]></description> <content:encoded><![CDATA[<p><strong>Nachtrag vom 19.08.2010:</strong> Ich wurde Opfer von &#8220;mod_security&#8221;, damit konnte ich keine Artikel mehr editieren, weil ich immer ein &#8220;404 Not Found&#8221; pr&#228;sentiert bekommen habe. DAS ist doch ein Heuler&#8230;</p><p>Nun, ich habe eine neue Version des Clients unten hingeh&#228;ng: Stabiler, so dass er auch eine ganze Nacht &#252;ber viele Stunden und Verbindungsprobleme hinweg weiterspielt.</p><p><img
src="http://blog.oncode.info/wp-content/uploads/2010/08/dice.png" alt="Wuerfel" title="dice" width="119" height="148" class="lead" align="left" />Oh nein. Frisch erk&#228;ltet wollte ich mich auf die Arbeiten st&#252;rzen, die eigentlich schon letztes Jahr h&#228;tten erledigt sein m&#252;ssen, und nun das: Das <a
href="http://www.linux-magazin.de/">Linux-Magazin</a> veranstaltet einen Wettbewerb f&#252;r Programmierer! Die Details sind in der Ausgabe 09/10 (auch <a
href="http://www.linux-magazin.de/Heft-Abo/Ausgaben/2010/09/Reiz-des-Mitmachens">online</a>) beschrieben und es gibt eine <a
href="http://wettbewerb.linux-magazin.de/index.php/Hauptseite">Wettbewerbs-Seite</a> dazu.</p><p>Grunds&#228;tzlich geht es darum, dass zwei Programme gegeneinander w&#252;rfeln. Wer zuerst 50 Punkte oder mehr erreicht, hat gewonnen. Ist man am Zuge, so kann man entweder W&#252;rfeln oder &#8220;Save&#8221;n. Bei einem Wurf werden die Augen zu der eigenen Punktzahl hinzugez&#228;hlt, ausser man w&#252;rfelt eine 6, dann werden alle Punkte seit dem letzten Save wieder abgezogen. Bei einem Save werden die Punkte gespeichert und der Gegner ist am Zuge.</p><p>Gespielt wird richtig Harte-M&#228;nner-m&#228;ssig &#252;ber einen TCP-Port mit einem definierten, menschenlesbaren Protokoll.</p><p>Die Programmiersprache kann frei gew&#228;hlt werden, insofern sie in Ubuntu 10.04 vorhanden ist und mit einem Makefile gebuildet und gestartet werden kann. Ich hab mich mal wieder f&#252;r PHP (Kommandozeile) entschieden weil das Problem, hmm, &#252;berhaupt nicht zeitkritisch ist (nein, auch diesmal kein <a
href="http://en.wikipedia.org/wiki/Monte_Carlo_method">Monte-Carlo</a>) und auch sonst, hmm, und &#252;berhaupt nicht weil ich zu faul war ein Makefile f&#252;r Java zu schreiben oder meine C-Kenntnisse aufzufrischen <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p><p>Als Erstes habe ich einen Simulator f&#252;r einen Benchmark implementiert. Als Messgr&#246;sse habe ich die Anzahl &#8220;Transaktionen&#8221; gew&#228;hlt, also wie oft mein Programm dran war mit W&#252;rfeln bis die 50 erreicht wurden. Dabei habe ich ein paar interessante Erkenntnisse gewonnen: So sind zum Beispiel die Resultate sehr unterschiedlich wenn zuf&#228;llig mit Wahrscheinlichkeit 1/4 oder wenn jedes 4. mal gesaved wird. Auch die optimale Wahrscheinlichkeit habe ich so nicht erwartet wie sie gemessen wurde.</p><p>Um meine Arbeit etwas zu teilen und um den Einstieg zu erleichtern, publiziere ich hier meine Spielerumgebung. Der geneigte Wettbewerbsteilnehme m&#252;sste nun nur noch:</p><ul><li>Den Spielernamen &#228;ndern (Konstante NAME),</li><li>Die Methode <tt>play</tt> mit Intelligenz f&#252;llen.</li></ul><p>Zuerst das <tt>Makefile</tt>:<br
/> [cc lang="Make"]<br
/> PHP = /usr/bin/php</p><p>build:<br
/> echo &#8220;PHP muss nit gebuildet werden <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .&#8221;<br
/> game: wettbewerb.php<br
/> $(PHP) ./wettbewerb.php<br
/> [/cc]</p><p>Und hier der Spieler <tt>wettbewerb.php</tt>, bereit getuned zu werden:<br
/> [cc lang="php"]<br
/> <?php<br
/> /**<br
/> * Wettbewerbsgeruest von Skaldrom Y. Sarg (http://blog.oncode.info)<br
/> */</p><p>define(&#8220;NAME&#8221;,&#8221;test.spieler&#8221;); // UNBEDINGT AENDERN!!!! Kleinbuchstaben, keine Sonderzeichen<br
/> define(&#8220;LOGFILE&#8221;,&#8221;dice.log&#8221;); // Schreibt keines wenn leer (fuer den Wettbewerb)<br
/> define(&#8220;SERVER&#8221;, &#8220;wettbewerb.linux-magazin.de&#8221;);<br
/> define(&#8220;PORT&#8221;, 3333);</p><p>class wettbewerb {</p><p> /**<br
/> * Dies ist die Spielfunktion.<br
/> *<br
/> * Macht was cleveres, aber stehlt mir nicht die Preise!<br
/> *<br
/> *<br
/> * @param $numThrowsThisTransaction int Anzahl W&#252;rfe seit letzter 6 oder letztem Save<br
/> * @param $currentTransactionPoints int Punkte seit letzter 6 oder letztem Save<br
/> * @param $pointsAtLastSave         int Punkte bei letztem Save<br
/> * @param $transactions             int Anzahl gemachter Z&#252;ge<br
/> * @param $numSaves                 int Anzahl Saves<br
/> * @param $enemyPoints              int Punkte des Gegners<br
/> * @param $rollsThisTransaction     Array W&#252;rfe in diesem Durchgang<br
/> *<br
/> * @return String &#8220;roll&#8221; f&#252;r einen Wurf, &#8220;save&#8221; um zu speichern und abzugeben.<br
/> */</p><p> function play($numThrowsThisTransaction, $currentTransactionPoints, $pointsAtLastSave, $transactions, $numSaves, $enemyPoints, $rollsThisTransaction) {<br
/> $this->log(&#8220;[PLAY-RESULT]: ROLL.&#8221;);<br
/> return &#8220;roll&#8221;;<br
/> }</p><p> function log($message) {<br
/> if(LOGFILE) {<br
/> error_log(date(&#8220;r&#8221;).&#8221;: &#8220;.$message.&#8221;\n&#8221;, 3, LOGFILE);<br
/> }<br
/> }</p><p> function secureFGetS() {<br
/> $response=&#8221;";<br
/> if(!$this->socket) {<br
/> do {<br
/> $this->connect();<br
/> } while(!$this->socket);<br
/> }<br
/> $response=fgets($this->socket);<br
/> $info = stream_get_meta_data($this->socket);<br
/> if ($info['timed_out']) {<br
/> $this->log(&#8220;Got a Socket Timeout! (unread:&#8221;.$info['unread_bytes'].&#8221;)&#8221;);<br
/> fclose($this->socket);<br
/> $this->socket=0;<br
/> $response=&#8221;";<br
/> sleep(5);<br
/> }<br
/> return $response;</p><p> }</p><p> function connect() {<br
/> // Kontaktaufnahme<br
/> $this->socket=0;<br
/> while(!$this->socket) {<br
/> $this->log(&#8220;CONNECTING&#8221;);<br
/> $this->socket = @fsockopen(SERVER , PORT, $errno , $errstr , 180);<br
/> if(!$this->socket) {<br
/> $this->log(&#8220;Verbindungsversuch fehlgeschlagen: ($errno) $errstr.&#8221;);<br
/> sleep(5);<br
/> }<br
/> }<br
/> if(function_exists(&#8220;stream_set_timeout&#8221;)) {<br
/> $this->log(&#8220;Set Socket Timeout.&#8221;);<br
/> stream_set_timeout($this->socket, 120); //two Mins<br
/> }<br
/> // Antwort des Servers<br
/> $this->log(&#8220;CONNECTED&#8221;);<br
/> }</p><p> function playARound() {<br
/> $this->log(&#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; NEW GAME &#8220;.SERVER .&#8221;:&#8221;.PORT.&#8221; &#8212;&#8212;&#8212;&#8212;&#8212;&#8211;&#8221;);</p><p> do {<br
/> $this->connect();<br
/> } while(!$this->socket);<br
/> do {<br
/> $response=$this->secureFGetS();<br
/> } while (!$response);<br
/> $resInfo=split(&#8221; &#8220;, $response);<br
/> $serverCommand=strtoupper(trim($resInfo[0]));<br
/> $this->log(&#8220;[SERVER]: &#8220;.trim($response).&#8221; (&#8220;.$serverCommand.&#8221;)&#8221;);<br
/> switch($serverCommand) {<br
/> case &#8220;HELO&#8221;: break; // Alles OK<br
/> case &#8220;DENY&#8221;: fclose($this->socket);<br
/> return;<br
/> break; // Dann halt nicht<br
/> default: fclose($this->socket);<br
/> $this->log(&#8220;[CLIENT]: Exit on Hello because we do not understand [$serverCommand].&#8221;);<br
/> exit(); // DAS verstehen wir nicht!<br
/> }</p><p> // Authentifizierung<br
/> $message=&#8221;AUTH &#8220;.strtolower(NAME);<br
/> $this->log(&#8220;[CLIENT]: &#8220;.$message);<br
/> fputs($this->socket, $message.&#8221;\n&#8221;);</p><p> // Los gehts!<br
/> $currentPlayer=1; // Wer ist am Zug? 0 == wir, 1 == der Andere<br
/> $numThrowsThisTransaction=0;<br
/> $currentTransactionPoints=0;<br
/> $pointsAtLastSave=0;<br
/> $transactions=1;<br
/> $numSaves=0;<br
/> $enemyPoints=0;<br
/> $rollsThisTransaction=array();<br
/> do {<br
/> $response=&#8221;";<br
/> $response=$this->secureFGetS();<br
/> if(!$response) {<br
/> $this->log(&#8220;INGAME TIMEOUT: Forgetting this game.&#8221;);<br
/> return;<br
/> }<br
/> $this->log(&#8220;[SERVER]: &#8220;.trim($response));<br
/> $resInfo=split(&#8221; &#8220;, $response);<br
/> $serverCommand=strtoupper(trim($resInfo[0]));<br
/> switch($serverCommand) {<br
/> case &#8220;THRW&#8221;: // Ein Wurf<br
/> if($currentPlayer==0) { // Wir sind dran<br
/> if($resInfo[1]==6) { // Ooops, eine 6 gew&#252;rfelt<br
/> $currentPlayer=1; // Der Andere ist dran<br
/> $currentTransactionPoints=0;<br
/> $numThrowsThisTransaction=0;<br
/> $transactions++;<br
/> $rollsThisTransaction=array();<br
/> } else {<br
/> $numThrowsThisTransaction++;<br
/> $currentTransactionPoints+=$resInfo[1];<br
/> $rollsThisTransaction[]=$resInfo[1];<br
/> }<br
/> }<br
/> break;<br
/> case &#8220;DENY&#8221;: break; // Dann halt nicht<br
/> case &#8220;TURN&#8221;:<br
/> $currentPlayer=0; // Wir sind dran!<br
/> $enemyPoints=$resInfo[2];<br
/> $pointsCurrentOurs=$resInfo[1];<br
/> $currentTransactionPoints=$pointsCurrentOurs-$pointsAtLastSave;<br
/> // Here we play!<br
/> $ret=$this->play($numThrowsThisTransaction, $currentTransactionPoints, $pointsAtLastSave, $transactions, $numSaves, $enemyPoints, $rollsThisTransaction);<br
/> if(strtolower($ret==&#8221;roll&#8221;)) { // Rollin&#8217; Baby<br
/> $this->log(&#8220;[CLIENT]: ROLL&#8221;);<br
/> fputs($this->socket, &#8220;ROLL\n&#8221;);<br
/> } else { // Save<br
/> $this->log(&#8220;[CLIENT]: SAVE&#8221;);<br
/> fputs($this->socket, &#8220;SAVE\n&#8221;);<br
/> $transactions++;<br
/> $rollsThisTransaction=array();<br
/> $numThrowsThisTransaction=0;<br
/> $numSaves++;<br
/> $pointsAtLastSave=$pointsCurrentOurs;<br
/> $currentTransactionPoints=0;<br
/> $currentPlayer=1;<br
/> }<br
/> break;<br
/> case &#8220;DEF&#8221;:<br
/> case &#8220;WIN&#8221;: break;<br
/> default: fclose($this->socket);<br
/> $this->log(&#8220;[CLIENT]: Exit because we do not understand [$serverCommand].&#8221;);<br
/> exit(); // DAS verstehen wir nicht!<br
/> }<br
/> } while (!in_array($serverCommand, array(&#8220;DENY&#8221;, &#8220;DEF&#8221;, &#8220;WIN&#8221;)));<br
/> // Feddich Lustig<br
/> fclose($this->socket);<br
/> }</p><p>}</p><p>$w=new wettbewerb();<br
/> for($i=0;$i<1000;$i++) {<br
/> echo &#8220;*** $i ***\n&#8221;;<br
/> $w->playARound();<br
/> }<br
/> ?><br
/> [/cc]</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2010/08/10/ein-programmierwettbewerb-vom-linux-magazin/feed/</wfw:commentRss> <slash:comments>4</slash:comments> <series:name><![CDATA[Linux-Magazin Wettbewerb]]></series:name> </item> <item><title>Zeitintervalle (Erledigungsfristen) in PHP</title><link>http://blog.oncode.info/2009/12/13/zeitintervalle-erledigungsfristen-in-php/</link> <comments>http://blog.oncode.info/2009/12/13/zeitintervalle-erledigungsfristen-in-php/#comments</comments> <pubDate>Sun, 13 Dec 2009 17:26:40 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[intervall]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[zeit]]></category> <category><![CDATA[zeitintervall]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1445</guid> <description><![CDATA[Dieser Artikel zeigt wie man Erledigungsintervalle oder Fristen in PHP parst.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2009/12/time.png" alt="time" title="time" align="left" class="lead"/>Ich bin wiedereinmal am Coden und irgendwie scheine ich mit fortschreitendem Alter auf immer mehr ungel&#246;ste Probleme zu treffen. In der aktuellen Applikation geht es darum, dass Erledigungsfristen als Intervalle eingegeben werden k&#246;nnen: Beispielsweise muss eine Aufgabe immer in 2 Monaten, 1 Woche und 4 Sekunden nach dem Erfassen erledigt sein. Daraus ergeben sich ein Strauss an Problemen (oder ein Problemkorb, wie man neuerdings zu sagen scheint. Was f&#252;r eine schreckliche Vorstellung, nur FR&#220;CHTE-K&#246;rbe finde ich noch abschreckender):</p><ul><li>Jemand muss die Aufgabe erledigen. Zum Gl&#252;ck ist das ein PAL (Problem anderer Leute) <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</li><li>&#8220;In einem Monat&#8221; und &#8220;In einem Jahr&#8221; kann nicht trivial in Sekunden umgerechnet werden. Irgendein Soziopath hat mal bestimmt, dass nicht alle Monate und Jahre die gleiche Anzahl Tage haben. Ganz zu schweigen von Schaltsekunden und Sommer-/Winterzeit.</li><li>Aus hier zu verschweigenden Gr&#252;nden habe ich weder Lust noch Zeit ein GUI mit 7 Textfeldern f&#252;r Jahre, Monate, Wochen, Tage, Stunden, Minuten und Sekunden aufzubauen.</li></ul><p>Ich habe mich f&#252;r ein simples Eingabefeld entschieden, in denen das Erledigungsintervall in der Form &#8220;Zahl Zeitbezeichner Zahl Zeitbezeichner &#8230;&#8221; eingegeben werden kann. In obigem Beispiel w&#228;re das: &#8220;<tt>2m 1w 4s</tt>&#8220;.</p><p>Leider ist die Aufgabe ein Bisschen zu klein um einen <a
href="http://blog.oncode.info/2007/10/25/eine-eigene-programmiersprache-erschaffen-lexer-und-parser-in-php/">richtigen Parser</a> hochzufahren <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> , und so habe ich die kleineren Kanonen hervorgeholt. Meine Idee is die Folgende: Dieses Intervall wird in einen String &#252;bersetzt, der direkt an <a
href="http://php.net/manual/en/function.strtotime.php">strtotime</a> &#252;bergeben werden kann. Soll sich diese Funktion doch um die H&#228;sslichkeiten der Zeit k&#252;mmern.</p><p>Mein Ansatz ist zweistufig: Erst wird geparst und ein Array aus den Tokens erzeugt. Im Idealfall also immer abwechselnd &#8220;number&#8221; und &#8220;alpha&#8221;. Im zweiten Schritt wird dann der String daraus geformt. Hier der Code f&#252;r alle armen Seelen, die vielleicht mal auf dasselbe exotische Problem treffen sollten. Mir ist bewusst, dass ich auch mit RegExpen h&#228;tte arbeiten k&#246;nnen, aber die sind eh nur f&#252;r Weicheier <img
src='http://blog.oncode.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> :</p><p>[cc lang="php"]<br
/> /**<br
/> * Parses a string with an interval into a string that is understood by strtotime.<br
/> *<br
/> * Example: &#8220;3w 2d 4h 2s&#8221; means 3 Weeks, 2 days, 4 hours<br
/> */<br
/> function timeIntervalToStrtotime($timeInterval) {<br
/> $tokens=array();<br
/> $currentTokenType=&#8221;";<br
/> $currentToken=&#8221;";</p><p> // Poor man&#8217;s scanner<br
/> for($i=0; $i<strlen($timeInterval); $i++) {</p><p> // Ignore whitespaces<br
/> if(ctype_space($timeInterval[$i])) {<br
/> continue;<br
/> }</p><p> // Numbers or names<br
/> $thisTokenType=ctype_digit($timeInterval[$i])?&#8221;numeric&#8221;:&#8221;alpha&#8221;;</p><p> if($currentTokenType == $thisTokenType) {<br
/> $currentToken.=$timeInterval[$i];<br
/> } // Else<br
/> if($currentTokenType != $thisTokenType) { // New Tokentype<br
/> if($currentTokenType!=&#8221;") { // Not first token<br
/> // Save old token<br
/> $tokens[]= array(&#8220;type&#8221; => $currentTokenType, &#8220;token&#8221; => $currentToken);<br
/> } else { // First token must be numeric<br
/> if($thisTokenType!=&#8221;numeric&#8221;) {<br
/> return &#8220;&#8221;;<br
/> }<br
/> }<br
/> $currentTokenType=$thisTokenType;<br
/> $currentToken=$timeInterval[$i];<br
/> }<br
/> }<br
/> // Save last Token<br
/> if($currentTokenType!=&#8221;") {<br
/> $tokens[]= array(&#8220;type&#8221; => $currentTokenType, &#8220;token&#8221; => $currentToken);<br
/> }</p><p> // Poor man&#8217;s parser</p><p> // There has to be an even number of tokens<br
/> if(count($tokens) % 2 != 0) {<br
/> return &#8220;&#8221;;<br
/> }</p><p> $ret=array();<br
/> for($i=0; $i<count($tokens); $i+=2) {<br
/> switch(strtolower($tokens[$i+1]["token"])) {<br
/> case &#8220;a&#8221;:<br
/> case &#8220;y&#8221;:<br
/> case &#8220;yr&#8221;:<br
/> case &#8220;year&#8221;:<br
/> case &#8220;years&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; years&#8221;; break;<br
/> case &#8220;m&#8221;:<br
/> case &#8220;mon&#8221;:<br
/> case &#8220;month&#8221;:<br
/> case &#8220;months&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; months&#8221;; break;<br
/> case &#8220;w&#8221;:<br
/> case &#8220;week&#8221;:<br
/> case &#8220;weeks&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; weeks&#8221;; break;<br
/> case &#8220;d&#8221;:<br
/> case &#8220;day&#8221;:<br
/> case &#8220;days&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; days&#8221;; break;<br
/> case &#8220;h&#8221;:<br
/> case &#8220;hr&#8221;:<br
/> case &#8220;hour&#8221;:<br
/> case &#8220;hours&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; hours&#8221;; break;<br
/> case &#8220;m&#8221;:<br
/> case &#8220;min&#8221;:<br
/> case &#8220;mins&#8221;:<br
/> case &#8220;minute&#8221;:<br
/> case &#8220;minutes&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; minutes&#8221;; break;<br
/> case &#8220;s&#8221;:<br
/> case &#8220;sec&#8221;:<br
/> case &#8220;secs&#8221;:<br
/> case &#8220;second&#8221;:<br
/> case &#8220;seconds&#8221;: $ret[]=&#8221;+&#8221;.$tokens[$i]["token"].&#8221; seconds&#8221;; break;<br
/> default: return &#8220;&#8221;;<br
/> }<br
/> }<br
/> return join(&#8221; &#8220;, $ret);<br
/> }</p><p>$tests=array(&#8220;1s&#8221;, &#8220;1&#8243;, &#8221; 1y 2d 3 mins 4sec&#8221;, &#8221; 15 yr &#8220;, &#8220;a2 3mon&#8221;);<br
/> foreach($tests as $test) {<br
/> echo &#8220;TEST:[$test]: &#8220;.timeIntervalToStrtotime($test).&#8221;\n&#8221;;<br
/> }<br
/> [/cc]</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2009/12/13/zeitintervalle-erledigungsfristen-in-php/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>m:n Beziehungen zu Lookup-Tables mit einem SQL-Befehl erstellen</title><link>http://blog.oncode.info/2009/11/11/mn-beziehungen-zu-lookup-tables-mit-einem-sql-befehl-erstellen/</link> <comments>http://blog.oncode.info/2009/11/11/mn-beziehungen-zu-lookup-tables-mit-einem-sql-befehl-erstellen/#comments</comments> <pubDate>Wed, 11 Nov 2009 07:35:40 +0000</pubDate> <dc:creator>skaldrom</dc:creator> <category><![CDATA[Theorie und Schnipsel]]></category> <category><![CDATA[insert]]></category> <category><![CDATA[Lookup]]></category> <category><![CDATA[MySQL]]></category> <category><![CDATA[select]]></category> <category><![CDATA[SQL]]></category> <guid
isPermaLink="false">http://blog.oncode.info/?p=1380</guid> <description><![CDATA[Mei m:n Verbindungen mit Lookup-Tables k&#246;nnen die Verbindungen in einem SQL-Statement gemacht werden. Dieser Beitrag zeigt wie.]]></description> <content:encoded><![CDATA[<p><img
src="http://blog.oncode.info/wp-content/uploads/2009/11/puzzle.png" alt="puzzle" title="puzzle" width="186" height="147" class="lead" align="left"/>Bei diesen Beitrag habe ich echt &#252;berlegt, ihn unter einem anderen Namen zu ver&#246;ffentlichen. Ist der Inhalt doch eigentlich sowas von trivial und f&#252;r Menschen mit mehr als zwei Hirnzellen wahrscheinlich sowieso offensichtlich. Aber das Gef&#252;hl, das mich durchflutet hat als mich Erkenntnis k&#252;sste war so erf&#252;llend und erhebend, dass ich dieses gerne mit Euch teilen w&#252;rde.</p><p>SQL ist eine geniale Erfindung. Zwar wird es im Moment arg von den schm&#228;chtigen <a
href="http://en.wikipedia.org/wiki/NoSQL">noSQL</a> bedr&#228;ngt, aber es wird wie triumphieren *star-wars-musik-on*, denn die Anderen werden untergehen wie die noAngels, es wird gewinnen wie die Maden &#252;ber die noMaden, h&#228;ufiger verwendet werden, so wie es das Ah gegen&#252;ber noAh wird und edler sein, wie der Orden gegen&#252;ber noRden! Ich bin heute noch auf der Suche nach der vern&#252;nftigen Frage, die nicht in ein einziges SQL-Statement verpackt werden kann.*star-wars-musik-off*</p><h3>Beispiel-Schema</h3><p>Doch zur&#252;ck zum Problem: Es seien Entit&#228;ten gegeben, die m:n Beziehungen zu &#8220;lookup-Werten&#8221; haben. Ein bestehender Wert in einem Set also, das sich selten &#228;ndert wie beispielsweise Kategorien, Zust&#228;nde, Farben, L&#228;ndercodes, Geschlechter oder so etwas. Ein weiterer Use Case w&#228;re ein Import, bei dem die Datens&#228;tze schon erstellt wurden und nun nur noch die Verbindungen fehlen.</p><p>Hier ein Beispiel:<br
/> <strong>Personen</strong> beinhalten, ja ratet mal, Personen.Hier vereinfacht nur mit Namen und Vornamen.<br
/> <strong>Personentypen</strong> bezeichnen die Art der Person. Beispielsweise <em>Lernender</em>, <em>Lehrperson</em>, <em>Verwaltungsangestellter</em> oder auch <em>Schulleiter</em>.</p><p>Jede Person kann mehreren Personentypen entsprechen. Diagnose: klassisches m:n mit Verbindungstabelle (<strong>personen_personentypen</strong>).</p><p><a
href="http://blog.oncode.info/wp-content/uploads/2009/11/erd.png"><img
src="http://blog.oncode.info/wp-content/uploads/2009/11/erd-300x82.png" alt="ERD-Diagramm einer m:n Beziehung" title="ERD-Diagramm einer m:n Beziehung" width="300" height="82" class="alignnone size-medium wp-image-1381" /></a></p><p>Die Inhalte der Tabellen gestalten sich folgendermassen:</p><p><strong>Personen</strong></p><pre>
+----+---------------+-----------+
| id | name          | vorname   |
+----+---------------+-----------+
|  1 | Schinkler     | Sebastian |
|  2 | Moser         | Hanspeter |
|  3 | Gublinsbacher | Anneli    |
|  4 | Iltis         | Mahara    |
+----+---------------+-----------+
</pre><p><strong>Personentypen</strong></p><pre>
+----+-------------------------+
| id | personentyp             |
+----+-------------------------+
|  1 | Lernender               |
|  2 | Lehrperson              |
|  3 | Verwaltungsangestellter |
|  4 | Schulleiter             |
+----+-------------------------+
</pre><h3>Insert kombiniert mit Select</h3><p>Der alte Ablauf war folgendermassen:</p><ol><li>Lese ID von Personen.</li><li>Lese ID von Personentypen.</li><li>F&#252;ge einen neuen Verbindungsdatensatz in personen_personentypen ein.</li></ol><p>Doch es geht viel einfacher! Die Werte, die in personen_personentypen eingef&#252;gt werden sollen, k&#246;nnen direkt im Statement selektiert werden. Grunds&#228;tzlich f&#252;gt man also das Resultat einer Abfrage ein:</p><p>[cc lang="sql"]<br
/> INSERT INTO personen_personentypen<br
/> SELECT p.id, pt.id FROM personen p, personentypen pt<br
/> WHERE p.name=&#8217;Moser&#8217; AND p.vorname=&#8217;Hanspeter&#8217; AND pt.personentyp=&#8217;Lehrperson&#8217;<br
/> [/cc]</p><p>Wirklich effizient wird es, wenn man gerade mehrere Lookupwerte verbindet:<br
/> [cc lang="sql"]<br
/> INSERT INTO personen_personentypen<br
/> SELECT p.id, pt.id FROM personen p, personentypen pt<br
/> WHERE p.name=&#8217;Gublinsbacher&#8217; AND p.vorname=&#8217;Anneli&#8217; AND pt.personentyp IN(&#8216;Verwaltungsangestellter&#8217;, &#8216;Schulleiter&#8217;)<br
/> [/cc]</p><p>Jaaa, mit so einfachen Dingen kann man mich gl&#252;cklich und zufrieden machen&#8230;</p> ]]></content:encoded> <wfw:commentRss>http://blog.oncode.info/2009/11/11/mn-beziehungen-zu-lookup-tables-mit-einem-sql-befehl-erstellen/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> </channel> </rss>
