{"id":2128,"date":"2015-10-11T11:40:44","date_gmt":"2015-10-11T10:40:44","guid":{"rendered":"https:\/\/u-labs.de\/blog\/?p=2128"},"modified":"2015-10-11T11:44:10","modified_gmt":"2015-10-11T10:44:10","slug":"template-php-mit-mvc-ohne-framework","status":"publish","type":"post","link":"https:\/\/u-labs.de\/portal\/template-php-mit-mvc-ohne-framework\/","title":{"rendered":"Vorlage und Anleitung: PHP mit MVC ohne Framework"},"content":{"rendered":"<p><a href=\"http:\/\/de.wikipedia.org\/wiki\/Model_View_Controller\" target=\"_blank\" rel=\"nofollow\">MVC<\/a> ist ein bew\u00e4hrtes Muster zur Entwicklung von Anwendungen. Es\u00a0schafft \u00dcbersicht und bringt eine gewisse Struktur in die Entwicklung. Soll eine PHP-Webanwendung nach dem MVC-Prinzip entwickelt werden,\u00a0ist der Gedanke \u00fcber ein\u00a0MVC-Framework oft nicht mehr weit entfernt. Doch da fangen die Probleme bereits an: Welches Framework eignet sich am besten?\u00a0Ein Bolide wie das Zend Framework? Oder doch lieber eine leichtgewichtigere Alternative wie CodeIgniter? Ist die Dokumentation brauchbar? \u00a0Und wie sieht es eigentlich mit\u00a0Support\/Kompabilit\u00e4t, geschweige denn einer Template-Engine aus?<\/p>\n<p>Keine einfache Entscheidung. Frameworks haben durchaus ihre Daseinsberechtigung. Aber insbesondere bei kleineren Anwendungen ist ein umfangreiches Framework einfach zuviel des Guten und bremst die Anwendung und Entwicklung eher aus als sie zu f\u00f6rdern. Grunds\u00e4tzlich braucht man zwar \u00fcberhaupt kein Framework um nach dem MVC-Prinzip in PHP zu entwickeln. Aber eine Art\u00a0<em>Micro-Framework<\/em>\u00a0erleichtert die Arbeit in der Tat ungemein. Folgender Artikel stellt eines zusammen.<\/p>\n<h3>Struktur und Namenskonventionen<\/h3>\n<p>Zu Beginn macht es Sinn, sich Gedanken \u00fcber die Verzeichnisstruktur zu machen. Die Folgende hat sich\u00a0bew\u00e4hrt:<\/p>\n<p>-Model\n-View\n&#8211;Global\n-Controller\n-Include\n-Clientscript<\/p>\n<p>JavaScript und CSS kann man nat\u00fcrlich in getrennte Ordner legen, mache ich jedoch bewusst nicht: Sie geh\u00f6ren oft zusammen und so viele gibt es davon auch wieder nicht, dass die \u00dcbersicht durch eine Aufteilung deutlich verbessert werden w\u00fcrde.\u00a0Clientseitige Frameworks wie Bootstrap kommen meist ohnehin in einem eigenen Ordner daher.\u00a0Diese aufzuteilen ist erfahrungsgem\u00e4\u00df keine gute Idee, da beispielsweise in CSS-Dateien nicht selten auf weitere Ressourcen wie Schriften oder Grafiken verwiesen wird, welche die originale Verzeichnisstruktur voraussetzen. Der Aufwand \u00fcberwiegt daher dem Nutzen.<\/p>\n<p>F\u00fcr die Bezeichnungen von Ordnern und Dateien sollte zu Beginn eine\u00a0<strong>Namenskonvention<\/strong> aufgestellt und nat\u00fcrlich auch eingehalten werden. Das erm\u00f6glicht sp\u00e4ter nicht nur Automatisierung, sondern schafft \u00dcbersicht und vermeidet Fehler.\u00a0F\u00fcr diesen Artikel sowie das dazugeh\u00f6rige Template gilt folgende Namenskonvention:<\/p>\n<ul>\n<li><strong>UpperCamelCase<\/strong> f\u00fcr Dateien, Ordner und Klassen (z.B.\u00a0<strong>Config.php<\/strong>, <strong>class Config<\/strong>)<\/li>\n<li><strong>lowerCamelCase<\/strong> f\u00fcr Attribute, Funktionen und Variablen (z.B.\u00a0<strong>private $content<\/strong>,\u00a0<strong>public function getContent()<\/strong>)<\/li>\n<li><strong>Controller<\/strong> erhalten ein Controller-Prefix in Dateiname und Klasse (z.B. <strong>class ControllerHome<\/strong>, <strong>ControllerHome.php<\/strong>). Funktionsnamen entsprechen denen der Actions.<\/li>\n<li><strong>Views<\/strong> erhalten ein View-Prefix im Dateinamen(z.B. <strong>ViewHome<\/strong>) und werden in Unterordnern strukturiert, die nach dem dazugeh\u00f6rigen Controller benannt werden &#8211; Seitenbestandteile die von allen Controllern geteilt werden geh\u00f6ren in den Unterordner <strong>Global<\/strong><\/li>\n<li><strong>Klassen<\/strong> werden gleich benannt wie die dazugeh\u00f6rige Datei, ohne Pr\u00e4fixe (z.B.\u00a0<strong>class User<\/strong> =&gt;\u00a0<strong>User.php<\/strong>)<\/li>\n<\/ul>\n<h3>Kontrolliere die Controller<\/h3>\n<p>Wer an seine Anfangszeit als PHP-Entwickler zur\u00fcckdenkt, wird sich noch gut an Konstrukte nach dem folgenden Schema erinnern k\u00f6nnen:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nif( $_GET&#x5B;'page'] == 'home' ) {\r\n    \/\/ Inhalt der Home-Seite laden\r\n}else if( $_GET&#x5B;'page'] == 'contact' ) {\r\n    \/\/ Inhalt der Kontakt-Seite laden\r\n}\r\n\/\/ &#x5B;...]\r\n<\/pre>\n<p>Sp\u00e4testens wenn die Anwendung im Laufe der Zeit umfangreicher wurde, hatte man am Ende eine riesige Datei, die nur aus solchen If-Elseif bzw. Switch-Statements besteht &#8211; Vorzugsweise die Indexseite. Das ist un\u00fcbersichtlich und schafft Arbeit. Warum also nicht diese l\u00e4stige Routine automatisieren? PHP-Frameworks arbeiten hier h\u00e4ufig mit Routen. Etwas vom Ansatz her vergleichbares l\u00e4sst sich jedoch auch recht einfach selbst umsetzen. Dazu ist es wie in der Namenskonvention vorgegeben n\u00f6tig, dass jede Action \u00fcber eine gleichnamige \u00f6ffentliche Methode im dazugeh\u00f6rigen Controller verf\u00fcgt.<\/p>\n<p><strong>Ein Beispiel:<\/strong><\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nclass ControllerHome {\r\n    public function index() {\r\n        \/\/ View der Index-Seite laden und ausgebe\r\n    }\r\n}\r\n<\/pre>\n<p>Wenn wir nun eine Anfrage an den Controller\u00a0<strong>Home<\/strong> mit der Action <b>Index<\/b> bekommen, soll eine Instanz der Klasse\u00a0<strong>ControllerHome<\/strong> erzeugt und die Funktion\u00a0<strong>index()<\/strong> aufgerufen werden. Dank der Flexibilit\u00e4t von PHP l\u00e4sst sich dies mit wenigen Zeilen Code automatisieren:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/**\r\n * Verarbeitet einen Controller-Action Request\r\n *\/\r\nclass RequestHandler {\r\n    private $defaultController;\r\n    private $defaultAction;\r\n\r\n    \/**\r\n     * @param $defaultController    Standardcontroller der aufgerufen werden soll, wenn keiner \u00fcbergeben wurde\r\n     * @param $defaultAction        Standardaction des Standardcontrollers\r\n     *\/\r\n    public function __construct( $defaultController, $defaultAction ) {\r\n        $this-&gt;defaultController = $defaultController;\r\n        $this-&gt;defaultAction = $defaultAction;\r\n    }\r\n\r\n    \/**\r\n     * Verarbeitet eine eingehende CA-Anfrage\r\n     * @param $get      HTTP-GET Array\r\n     * @param $post     HTTP-POST Array\r\n     *\/\r\n    public function handleRequest( $get, $post ) {\r\n        \/\/ POST und GET zusammenf\u00fchren\r\n        $request = array_merge( $get, $post );\r\n        \/\/ Pr\u00fcfen ob Controller und Action gesetzt sind, ansonsten Standardwerte verwenden\r\n        $controller = 'Controller' . ( isset( $request&#x5B;'controller'] ) ? $request&#x5B;'controller'] : $this-&gt;defaultController );\r\n        $action = isset( $request&#x5B;'action'] ) ? $request&#x5B;'action'] : $this-&gt;defaultAction;\r\n        if( class_exists( $controller ) ) {\r\n            $controllerInstance = new $controller( $request );\r\n            \/\/ Action aufrufen, sofern diese existiert\r\n            if( method_exists( $controllerInstance, $action ) ) {\r\n                $controllerInstance-&gt;{$action}();\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>In der index.php kann dies wie folgt eingebaut werden:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\ndefine( 'BASE_DIR', __DIR__ );\r\n\/\/ RequestHandler laden, der die CA-Anfrage bearbeitet\r\n$requestHandler = new RequestHandler( 'home', 'index' );\r\n$requestHandler-&gt;handleRequest( $_GET, $_POST );\r\n<\/pre>\n<p>Ein Aufruf von index.php?controller=home&amp;action=index w\u00fcrde die Klasse <strong>ControllerHome<\/strong>, also unseren Home-Controller, instanziieren und die Funktion <strong>index()<\/strong> aufrufen. Funktionieren wird der Code jedoch in dieser Form noch nicht: PHP wird sich beklagen, dass die Klasse <strong>ControllerHome<\/strong> nicht existiert &#8211; Zu recht, wurde die Datei <strong>Controller\/ControllerHome<\/strong> nicht per include eingebunden.\u00a0Das wurde bewusst \u00fcbersehen, weil wir dieses Problem im n\u00e4chsten Schritt ein f\u00fcr alle Mal l\u00f6sen.<\/p>\n<h3>Mit Autoload geht alles besser<\/h3>\n<p>Die Zeiten von manuellen Includes sind vorbei: Seit Version 5.1 bietet PHP die M\u00f6glichkeit, Klassen automatisch einzubinden, wenn diese ben\u00f6tigt werden. Warum sollte man das nur f\u00fcr Controller nutzen, und nicht gleich f\u00fcr andere ben\u00f6tigte Dateien wie etwa die Views ebenfalls? Zum Einsatz kommt daher die bereits <a href=\"https:\/\/u-labs.de\/blog\/autoload-php-klassen-automatisch-laden\/\" target=\"_blank\" rel=\"nofollow\">in einem anderen Beitrag vorgestellte AutoLoad-Klasse<\/a>. Mit ihrer Hilfe k\u00f6nnen PHP-Dateien aus verschiedenen Ordnern durch die Definition von Regeln geladen werden:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\ndefine( 'BASE_DIR', __DIR__ );\r\ndefine( 'INCLUDE_DIR', BASE_DIR . '\/Include' );\r\n\/\/ AutoLoad initialisieren\r\nrequire_once INCLUDE_DIR . '\/AutoLoader.php';\r\n$autoLoader = new Autoloader();\r\n\/\/ Klassen im Includes-Ordner direkt anhand ihres Namens ohne Prefix automatisch einbinden\r\n$autoLoader-&gt;addLoadRule( '', '.php', INCLUDE_DIR );\r\n\/\/ Controller automatisiert laden (Schema: Controller${NAME}.php)\r\n$autoLoader-&gt;addLoadRule( '', '.php', BASE_DIR . '\/Controller' );\r\n$autoLoader-&gt;startWork();\r\n<\/pre>\n<p>In diesem Fall gibt es zwei Regeln: Die erste l\u00e4dt alle Controller. Da diese bereits das Controller-Prefix im Klassennamen erhalten (<strong>Controller<\/strong>Home), ist hier keine Angabe eines Pr\u00e4fixes n\u00f6tig. Die Klasse\u00a0<strong>ControllerHome.php<\/strong>\u00a0wird aus <b>Controller\/ControllerHome.php<\/b> geladen.<\/p>\n<p>Die andere Regel gilt Allgemein f\u00fcr Klassen im Includes-Ordner. Auch hier sind Klassenname und Dateiname identisch: Eine Klasse namens\u00a0<strong>User<\/strong> w\u00fcrde beispielsweise im Pfad\u00a0<strong>Includes\/User.php<\/strong> gespeichert werden. Der Scan verl\u00e4uft rekursiv, somit werden Unterordner ebenfalls ber\u00fccksichtigt. Die Klasse User w\u00fcrde also beispielsweise auch im Pfad\u00a0<strong>Includes\/User\/User.php<\/strong>\u00a0gefunden werden.<\/p>\n<p>Nun k\u00f6nnen wir mit Klassen arbeiten, ohne diese Laden zu m\u00fcssen &#8211; das erledigt der AutoLoader f\u00fcr uns. Der Request-Handler ist somit einsatzbereit. Er muss allerdings\u00a0<strong>nach<\/strong> dem AutoLoader initialisiert werden, damit seine Klasse automatisch geladen wird:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/\/ &#x5B;...]\r\n$autoLoader-&gt;startWork();\r\n\r\n\/\/ RequestHandler laden, der die CA-Anfrage bearbeitet\r\n$requestHandler = new RequestHandler( 'Home', 'Index' );\r\n$requestHandler-&gt;handleRequest( $_GET, $_POST );\r\n<\/pre>\n<p>Beim Instanziieren\u00a0des <strong>RequestHandlers <\/strong> wird eine Standardroute angegeben, die geladen werden soll, wenn weder Controller noch Action angegeben wurden &#8211; Also bei einem Aufruf der Seite ohne Parameter. In diesem Fall w\u00e4re das der Controller\u00a0<strong>Home<\/strong> mit der Action\u00a0<strong>Index<\/strong>. Gro\u00df\/Kleinschreibung spielt auch hier keine Rolle, da Actions automatisch in Kleinbuchstaben umgewandelt werden.<\/p>\n<h3>Es werde View: Die Ansichten<\/h3>\n<p>Funktionsf\u00e4hig ist die Anwendung damit jedoch immer noch nicht, weil es nichts anzuzeigen gibt: Die Views werden nicht geladen. Und selbst wenn w\u00fcrde das nichts bringen, weil wir noch gar keine haben. Auch das Laden der View l\u00e4sst sich weitestgehendst automatisieren. Wir gehen davon aus, dass jede Action f\u00fcr gew\u00f6hnlich eine eigene View hat und diese gem\u00e4\u00df Namenskonvention gleich hei\u00dft wie die Action des Controllers. Da jede Ansicht aus einem Controller heraus abgerufen werden muss, macht es Sinn diese Funktionalit\u00e4t in einer Basisklasse unterzubringen:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/**\r\n * Stellt Funktionen global f\u00fcr alle Controller zur Verf\u00fcgung\r\n *\/\r\nclass BaseController {\r\n    protected $request;\r\n    \/\/ Vorangestellte Bezeichnung im Dateinamen von Controllern\r\n    private $controllerPrefix = 'Controller';\r\n\r\n    \/**\r\n     * Initialisiert den Basis-Controller\r\n     * @param $request  Ein Array von GET- und\/oder POST-Variablen\r\n     *\/\r\n    public function __construct( $request ) {\r\n        $this-&gt;request = $request;\r\n    }\r\n\r\n    \/**\r\n     * L\u00e4d eine View aus der Action des Controllers heraus, sofern sie existiert\r\n     * @param string $name  Name der zu ladenden View - Wenn dieser Parameter nicht angegeben wird, sucht die Funktion nach dem Namen der Action im Ordner des Controllers\r\n     *\/\r\n    public function view( $name = '') {\r\n        \/\/ Liefert Informationen \u00fcber die Klasse und Methode, von der aus diese Funktion aufgerufen wurde\r\n        $callerClassInfo = debug_backtrace()&#x5B;1];\r\n        \/\/ Der Name des Controllers muss um das Prefix bereinigt werden: ControllerHome =&gt; Home\r\n        $plainControllerName = substr( $callerClassInfo&#x5B;'class'], strlen( $this-&gt;controllerPrefix ) );\r\n        \/\/ Der Unterordner f\u00fcr die Views ist identisch mit dem Namen des Controllers ohne Prefix\r\n        $viewPath = VIEW_DIR . '\/' . $plainControllerName;\r\n        \/\/ Namen der aufrufenden Methode als Name der View nutzen, wenn keiner \u00fcbergeben wurde\r\n        if( strlen( $name ) == 0) {\r\n            $name = $callerClassInfo&#x5B;'function'];\r\n        }\r\n        \/\/ View laden\r\n        require_once INCLUDE_DIR . '\/FunctionsView.php';\r\n        loadView($name, $viewPath);\r\n    }\r\n}\r\n<\/pre>\n<p>Das eigentliche Einbinden der View wird allerdings in eine klassenlose Datei namens <strong>FunctionsView.php<\/strong> ausgelagert. Denn in den Views selbst m\u00fcssen weitere Views eingebunden werden k\u00f6nnen &#8211; Etwa um den Header oder Footer zu laden, diese Views sollen Global in allen anderen eingebunden werden k\u00f6nnen. Die Funktion <strong>loadView()<\/strong> wird daher ohne Klassenzuordnung in FunctionsView.php ausgelagert:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/**\r\n * L\u00e4d eine View\r\n * @param        $controllerName    Name des Controllers bzw. der View\r\n * @param string $folder            Pfad des Ordners, in dem sich die View befindet (Standardm\u00e4\u00dfig das Root-Verzeichnis der Views)\r\n *\/\r\nfunction loadView( $controllerName, $folder = VIEW_DIR ) {\r\n    \/\/ Mit der PathInfo werden Pfadangaben relativ zum View-Verzeichnis korrekt an den Ordner angeh\u00e4ngt wie z.B. Global\/Header\r\n    $pathInfo = pathinfo( $controllerName );\r\n    if( $pathInfo&#x5B;'dirname'] != '.' ) {\r\n        $folder .= '\/' . $pathInfo&#x5B;'dirname'];\r\n        $controllerName = $pathInfo&#x5B;'filename'];\r\n    }\r\n    \/\/ View laden und einbinden\r\n    $fullPath = AutoLoader::searchFile($folder, 'View' . $controllerName . '.php', false);\r\n    if( false !== $fullPath) {\r\n        require_once $fullPath;\r\n    }\r\n}\r\n<\/pre>\n<p>Doch zur\u00fcck zu unserem <strong>BaseController<\/strong>: Die Funktion <strong>view()<\/strong> kann parameterlos aufgerufen werden. Dann l\u00e4d sie automatisch die View, welche sich im Unterordner mit dem Namen des Controllers befinden muss und gleich wie die Action hei\u00dft. Die View zur Action <strong>Index<\/strong> im Controller <strong>Home<\/strong> ist somit im Verzeichnis <strong>View\/Home\/ViewIndex.php<\/strong> zu finden.<\/p>\n<p>Zum Aufruf der passenden View gen\u00fcgt somit eine Zeile Code:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nclass ControllerHome extends BaseController {\r\n    public function __construct( $request ) {\r\n        parent::__construct( $request );\r\n    }\r\n\r\n    public function index() {\r\n        return $this-&gt;view();\r\n    }\r\n}\r\n<\/pre>\n<p>Nat\u00fcrlich muss diese View nun angelegt werden, also im Pfad <strong>View\/Home\/ViewIndex.php<\/strong>. Wie oben bereits schon erw\u00e4hnt werden zumindest Footer und Header in der Regel zentral als eigene Views definiert und in die Seiten selbst lediglich eingebunden &#8211; Das vermeidet unn\u00f6tige Redundanz. Im Beispiel hier werden 3 Templates erstellt:<\/p>\n<p>Global\/ViewFooter.php\nGlobal\/ViewHeader.php\nGlobal\/ViewNavigation.php<\/p>\n<p>\u00dcber die Funktion <strong>loadView()<\/strong> k\u00f6nnen diese in der View der Seite (hier <strong>ViewIndex.php<\/strong>) eingebunden werden:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n&lt;?php loadView( 'Global\/Header'); ?&gt;\r\n\r\n&lt;body&gt;\r\n    &lt;?php loadView('Global\/Navigation'); ?&gt;\r\n\r\n    &lt;div class=&quot;container&quot;&gt;\r\n        &lt;h1&gt;Willkommen!&lt;\/h1&gt;\r\n        &lt;div style=&quot;font-size: 16px&quot;&gt;\r\n            Dies ist eine Beispiel-Seite zur Demonstration einer grundlegenden PHP-MVC Webanwendung ohne externes PHP-Framework.\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/body&gt;\r\n\r\n&lt;?php loadView( 'Global\/Footer' ); ?&gt;\r\n<\/pre>\n<p>Wie man sieht werden alle Pfade relativ zum View-Ordner angegeben und erfordern keine Dateierweiterung, das \u00fcbernimmt alles die loadView() Funktion. Und schon haben wir eine sehr einfache MVC-Anwendung in PHP entwickelt. Es fehlen lediglich Models, was jedoch schlicht und einfach an den fehlenden Daten liegt.<\/p>\n<h3>Eine Template-Engine?<\/h3>\n<p>Nun haben wir eine MVC-Anwendung in PHP bzw. das Grundger\u00fcst daf\u00fcr, aber keine Template-Engine. Oder etwa doch? Ich mag zwar grunds\u00e4tzlich das an C angelehnte Syntax mit den geschweiften Klammern. Aber zugegeben: In einer View sind Konstrukte wie das folgende Beispiel wirklich nicht das wahre.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n&lt;?php if( $user-&gt;isLoggedIn() ) { ?&gt;\r\n    Hallo &lt;?=$user-&gt;getUsername()?&gt;!\r\n&lt;?php } else { ?&gt;\r\n    Du bist nicht angemeldet!\r\n&lt;?php } ?&gt;\r\n<\/pre>\n<p>Es ist weder sch\u00f6n zu lesen noch zu schreiben. Das haben auch die PHP-Entwickler vor l\u00e4ngerem bereits bemerkt und daher bereits in PHP4 das sogenannte <a href=\"http:\/\/php.net\/manual\/de\/control-structures.alternative-syntax.php\" target=\"_blank\" rel=\"nofollow\">Alternative Syntax f\u00fcr Kontrollstrukturen<\/a> integriert. Es soll diesem Wirrwar von Klammern und PHP-Tags Einhalt gebieten. Das obige Beispiel sieht demnach wie folgt aus:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n&lt;?php if( $user-&gt;isLoggedIn() ): ?&gt;\r\n    Hallo &lt;?=$user-&gt;getUsername()?&gt;!\r\n&lt;?php else: ?&gt;\r\n    Du bist nicht angemeldet!\r\n&lt;?php endif; ?&gt;\r\n<\/pre>\n<p>Zwar etwas mehr Tipparbeit, aber trotzdem deutlich angenehmer und \u00fcbersichtlicher. In der ersten Bedingung sieht man au\u00dferdem eine weitere Neuerung: Der sogenannte\u00a0<strong>Short Open-Tag<\/strong> ist ab PHP 5.4 standardm\u00e4\u00dfig aktiviert. Zuvor musste er explizit \u00fcber die php.ini aktiviert werden, was jedoch kaum jemand tat. Daher ist es nicht verwunderlich, dass er praktisch nicht verwendet wurde. Immerhin hatte jeder Entwickler zu bef\u00fcrchten, dass seine Webanwendung dann auf einem Gro\u00dfteil der PHP-Webhoster ohne weiteres nicht funktionieren w\u00fcrden.<\/p>\n<p>Das ist schade, denn bei der Ausgabe von Variablen ist &lt;?= wirklich praktisch: Man spart sich nicht nur den vollen PHP-Tag der normal aus\u00a0<strong>&lt;?php<\/strong> besteht sondern auch das\u00a0<strong>echo<\/strong> zur Ausgabe sowie das Semikolon am Ende.\u00a0Diese Entwicklungen machen deutlich: PHP wurde eigentlich als Sprache f\u00fcr Templates entwickelt und wird auch in der Richtung weiterentwickelt. Zugegeben, mit der\u00a0Template-Engine\u00a0<strong>Smarty<\/strong> lie\u00dfe sich der obige Block noch weiter verk\u00fcrzen:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n{if $user-&gt;isLoggedIn()}\r\n    Hallo {$user.getUsername()}!\r\n{else}\r\n    Du bist nicht angemeldet!\r\n{\/if}\r\n<\/pre>\n<p>Die Frage welche man sich jedoch stellen sollte lautet: Sind es die paar gesparten Zeichen wert, eine doch recht umfangreiche Template-Engine einzusetzen? Zumindest bei kleineren Projekten d\u00fcrfte nicht selten festzustellen sein, dass die Boardmittel von PHP eigentlich v\u00f6llig ausreichen. Das verbessert nicht nur die Performance, sondern reduziert auch die Anzahl der Abh\u00e4ngigkeiten.<\/p>\n<h3>Fazit<\/h3>\n<p>Um kleinere PHP-Anwendungen nach dem MVC-Muster zu entwickeln, braucht es definitiv keine m\u00e4chtigen externen Frameworks. Im Einzelfall kann das nat\u00fcrlich Sinn machen, aber meist sind diese zu \u00fcberladen. Was PHP an Board hat reicht v\u00f6llig aus. Dazu unser selbst entwickeltes Micro-Framework, dass in der hier vorgestellten minimalen Form keine 200 Codezeilen umfasst.<\/p>\n<p>Das Beispiel aus diesem Artikel wurde zu einem Beispielprojekt mit 3\u00a0. Im gleichen Zuge kamen auch ein paar Hilfsfunktionen dazu, etwa zur Einbettung von JavaScript-Dateien oder Links in Templates. Darauf wurde in diesem Artikel nicht weiter eingegangen, da es mit dem Kernthema nichts zutun hat. Wer m\u00f6chte kann sich das Beispielprojekt im folgenden herunterladen.\u00a0Es ben\u00f6tigt lediglich PHP 5.4 oder h\u00f6her und l\u00e4uft problemlos in\u00a0Entwicklungsumgebungen wie etwa das bekannte Paket XAMPP.<\/p>\n<p><a href=\"https:\/\/u-labs.de\/wp-content\/uploads\/2015\/06\/PHP-MVC-Example.zip\">PHP-MVC-Example herunterladen (282 KB)<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>MVC ist ein bew\u00e4hrtes Muster zur Entwicklung von Anwendungen. Es\u00a0schafft \u00dcbersicht und bringt eine gewisse Struktur in die Entwicklung. Soll eine PHP-Webanwendung nach dem MVC-Prinzip entwickelt werden,\u00a0ist der Gedanke \u00fcber ein\u00a0MVC-Framework oft nicht mehr weit entfernt. Doch da fangen die Probleme bereits an: Welches Framework eignet sich am besten?\u00a0Ein Bolide wie das Zend Framework? Oder &#8230;<\/p>\n","protected":false},"author":5,"featured_media":2170,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61],"tags":[160,159,55,158],"class_list":["post-2128","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-softwareentwicklung","tag-framework","tag-mvc","tag-php","tag-php5"],"_links":{"self":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/2128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/comments?post=2128"}],"version-history":[{"count":29,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/2128\/revisions"}],"predecessor-version":[{"id":3162,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/2128\/revisions\/3162"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media\/2170"}],"wp:attachment":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media?parent=2128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/categories?post=2128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/tags?post=2128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}