<?xml version="1.0" encoding="utf-8" ?>

<rss version="2.0" 
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:admin="http://webns.net/mvcb/"
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
   xmlns:wfw="http://wellformedweb.org/CommentAPI/"
   xmlns:content="http://purl.org/rss/1.0/modules/content/"
   >
<channel>
    
    <title>Netz - Rettung - Recht (Artikel mit Tag PHP)</title>
    <link>https://netz-rettung-recht.de/</link>
    <description>Aus dem Leben eines Szlauszafs</description>
    <dc:language>de</dc:language>
    <generator>Serendipity 2.5.0 - http://www.s9y.org/</generator>
    <pubDate>Wed, 27 Apr 2022 06:33:55 GMT</pubDate>

    <image>
    <url>https://netz-rettung-recht.de/templates/2k11/img/s9y_banner_small.png</url>
    <title>RSS: Netz - Rettung - Recht - Aus dem Leben eines Szlauszafs</title>
    <link>https://netz-rettung-recht.de/</link>
    <width>100</width>
    <height>21</height>
</image>

<item>
    <title>Validierung von Eingaben im Kontaktformular</title>
    <link>https://netz-rettung-recht.de/archives/2411-Validierung-von-Eingaben-im-Kontaktformular.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/2411-Validierung-von-Eingaben-im-Kontaktformular.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=2411</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=2411</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Meine &lt;a href=&quot;https://th-h.de/&quot; title=&quot;Willkommen auf th-h.de! | th-h.de&quot;&gt;Homepage&lt;/a&gt; hat noch ein Kontaktformular. Selbstverständlich findet sich auf derselben Seite auch meine Mailadresse (samt GPG-Key), und man sollte annehmen, dass das der übliche Weg zur Kontaktaufnahme wäre, aber überraschend viele Nachrichten erreichen mich dann doch noch über das Kontaktformular. Dafür mag es verschiedene Gründe geben: der Mailclient auf dem Arbeitsrechner (oder dem Smartphone) hat vielleicht kein Profil mit der gewünschten Absenderadresse - oder für den Absender ist &amp;#8220;E-Mail&amp;#8221; gleichbedeutend mit &amp;#8220;GMail&amp;#8221; oder einem anderen webbasierten Dienst, d.h. es wird gar kein Mailprogramm mehr verwendet. Sei dem, wie dem sei - das Kontaktformular wird (trotz Recaptcha) durchaus genutzt, und ab und an verschluckte es sich an unerwarteten Eingaben. Gerne genommen war beispielsweise ein Komma im Eingabefeld für den Namen (&amp;#8220;Müller, Egon&amp;#8221;). Da das Formular die Eingabe einfach 1:1 in das From:-Feld übernommen hat, fand sich da dann etwas wie&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Müller, Egon &amp;lt;absender@domain.example&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Das aber gefällt der Technik nicht, denn ein &lt;code&gt;,&lt;/code&gt; trennt im From: oder To: mehrere Absender/Adressaten voneinander. Soll im Namen ein Komma vorkommen, muss der gesamte Name in &lt;code&gt;&quot;&lt;/code&gt; gesetzt werden.
Außerdem fiel mir bei dieser Gelegenheit auf, dass ich die Eingaben insgesamt nicht wirklich validiere. Zeit, das zu ändern.&lt;/p&gt;

&lt;p&gt;Und während ich noch überlegte, wie man noch einmal am besten eine Mail-Adresse validiert (ein wirklich nicht einfaches Unterfangen, aber mit Regular Expressions möglich, wenn man keine Kommentare akzeptiert), fiel mir wieder ein, dass ich so etwas doch schon einmal gebastelt hatte. Und richtig: da lag eine bereits halb vergessene PHP-Klasse herum, die Validierungsfunktionen bietet, u.a. die syntaktische Validerung einer Mailadresse. Und außerdem kann sie eine Eingabe auch gegen einen beliebigen regulären Ausdruck validieren.&lt;/p&gt;

&lt;p&gt;Also prüft das Kontaktformular jetzt, ob die E-Mail-Adresse syntaktisch gültig ist (weiterhin aber nicht, ob es diese Adresse gibt - was zumindest mit hoher Wahrscheinlichkeit möglich ist - oder ob sie dem Absender des Formulars gehört - was nicht möglich ist), wobei fast alle gültigen und jedenfalls alle real anzunehmenden Mailadressen akzeptiert werden, und es testet auch, ob der Absendername eines der Zeichen enthält, die nur zulässig sind, wenn der Name insgesamt in &lt;code&gt;&quot;&lt;/code&gt; steht. Dabei wird nicht jeder denkbare Fall abgefangen, aber immerhin die naheliegenden. Das ist schon einmal ein deutlicher Schritt nach vorne (und war nicht wirklich Aufwand, weil ich den Großteil der Vorarbeit bereits geleistet hatte).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Nachträglich veröffentlicht im April 2022.]&lt;/em&gt;&lt;/p&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/6c27a3ce741f4d0cbc668cde121aa4a9&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Thu, 24 Mar 2022 05:00:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/2411-guid.html</guid>
    <category>e-mail</category>
<category>php</category>

</item>
<item>
    <title>Naives MVC</title>
    <link>https://netz-rettung-recht.de/archives/2304-Naives-MVC.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/2304-Naives-MVC.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=2304</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=2304</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Seit 2014 setze ich sowohl bei neuen Websites als auch bei der &amp;#8220;Renovierung&amp;#8221; alter auf &lt;em&gt;static site generators&lt;/em&gt;, also &lt;a href=&quot;https://netz-rettung-recht.de/archives/1755-Generatoren-fuer-statische-Websites.html&quot; title=&quot;&quot;&gt;Generatoren für statische Webseiten&lt;/a&gt;, die aus Templates und Inhalten in verschiedenen Markups, aus Helper-Klassen und ggf. auch Datenbankinhalten HTML-Seiten generieren, die dann auf den Server hochgeladen werden. Das hat verschiedene Vorteile ggü. CMS oder selbstgeschriebenem Code: weil nur statisches HTML ausgeliefert wird, muss auf dem Server keine Scriptsprache wie PHP laufen, was die Last vermindert und vor allem auch aus Sicherheitsgründen angenehm ist, denn wo kein Code läuft, gibt es auch keine Sicherheitslücken. Außerdem bringen Generatoren wie &lt;em&gt;Nanoc&lt;/em&gt;, mein bevorzugtes Werkzeug, ein ganzes Toolset wie Helper-Klassen für häufige Aufgaben und Filter für die verschiedensten Markup-Formate mit. So habe ich mich daran gewöhnt, dass ich das Grundgerüst einer Website in Form von Templates, meistens in &lt;em&gt;HAML&lt;/em&gt;, erstelle, für die optische Gestaltung CSS-Frameworks wie &lt;em&gt;Bootstrap&lt;/em&gt; nutzen kann, wobei ein LESS-Compiler automatisch mitkommt, und dass ich die eigentlichen Inhalte in &lt;em&gt;Markdown&lt;/em&gt; (oder in einer Kombination aus &lt;em&gt;HAML&lt;/em&gt; und &lt;em&gt;Markdown&lt;/em&gt;) erstellen kann. Auf diese Weise habe ich mir einen gut funktionierenden &lt;a href=&quot;https://netz-rettung-recht.de/archives/1994-Workflow-fuer-die-Erstellung-und-Pflege-von-Webseiten.html&quot; title=&quot;&quot;&gt;Workflow&lt;/a&gt; geschaffen.&lt;/p&gt;

&lt;p&gt;Das alles erfüllt seinen Zweck im Grundsatz sehr gut, auch für in begrenztem Umfang dynamische Elemtente: es genügt ja, die Webseitengenerierung in regelmäßigen Abständen neu anzustoßen, bspw. nachts, oder wenn man will auch stündlich. &lt;em&gt;Nanoc&lt;/em&gt; kann man zudem aus Datenbanken speisen, und auch beim bloßen Umgang mit Textdateien lassen sich deren Metadaten auswerten, um bspw. die letzten Änderungen zu verlinken, Termine anzukündigen (aber nur, solange sie noch bevorstehen), Zeitangaben automatisch zu ersetzen (&amp;#8220;Diese Seiten stehen schon seit x Jahren online&amp;#8221;) und anderes mehr. Man kann auch mal ein PHP-Script dazwischenmixen, beispielsweise für ein Kontaktformular. Ihre Grenzen erreicht die Technik erst, wenn es um tatsächlich dynamische Seiten geht, also bspw. die Suche in Datenbanken (bei der man die Einträge nicht schlicht insgesamt vorab als statische Seiten erzeugen kann) - oder wenn ein Passwortschutz (jenseits von &lt;em&gt;Basic Auth&lt;/em&gt;) oder ein Session-Management implementiert werden soll. Dann braucht es etwas anderes. Nur was?&lt;/p&gt;

&lt;p&gt;Bestimmt fünf Jahre lang habe ich die Überarbeitung verschiedener Webseiten vor mir hergeschoben, deren Anforderungen über die Möglichkeit von &lt;em&gt;static site generators&lt;/em&gt; hinausgehen. Der ursprüngliche Ansatz aus meinen ersten (und zweiten) Schritten mit PHP - jede Seite ein Mix von HTML und PHP, letzteres vor allem Funktionen zum Generieren der Seitenstruktur (Header, Footer) und Navigation als &amp;#8220;Templating für Arme&amp;#8221; - kam natürlich nicht mehr in Betracht; natürlich machen allein &lt;a href=&quot;https://netz-rettung-recht.de/archives/1821-Templates-und-Template-Engines.html&quot; title=&quot;&quot;&gt;Templates&lt;/a&gt; in diesem Zusammenhang Sinn. Aber ich wollte die Inhalte auch nicht mehr in HTML verfassen, sondern zumindest in &lt;em&gt;Markdown&lt;/em&gt;; und ein CSS-Baukasten wie &lt;em&gt;Bootstrap&lt;/em&gt; sollte sich auch einbinden lassen, was einen Preprocessor wie LESS oder SASS zumindest wünschenswert macht. Über die Jahre habe ich mit vielen Gedanken gespielt, bspw. an &lt;a href=&quot;https://netz-rettung-recht.de/archives/1822-Webseiten-und-Webapplikationen-erstellen-und-pflegen.html&quot; title=&quot;&quot;&gt;Frameworks&lt;/a&gt; wie &lt;em&gt;Laravel&lt;/em&gt; oder &lt;em&gt;Symfony&lt;/em&gt;; früher hatte ich über viele Jahre die &lt;em&gt;CakePHP&lt;/em&gt;-Mailingliste abonniert. Das erschien mir dann aber doch vergleichsweise aufwendig, um letztlich einen Passwortschutz und ein paar Formulare bzw. Datenbankausgaben zu implementieren.&lt;/p&gt;

&lt;p&gt;Nach langer Pause und Stagnation wollte ich das Thema dieses Jahr über die Osterzeit aber noch einmal angehen. Dazu gehörten mehrere Schritte: Zunächst waren einige eng verbundene Webseiten zu entkoppeln, die gemeinsame Ressourcen nutzten, also bspw. alle dieselbe CSS-Datei eingebunden hatten. Danach brauchte es ein Design, das optisch den bisherigen Webseiten ähnlich sein sollte, nur schicker und möglichst unter Verwendung von &lt;em&gt;Bootstrap&lt;/em&gt; - und dafür wiederum musste ich mich mit &lt;em&gt;Bootstrap 5&lt;/em&gt; auseinandersetzen (meine bisherigen Webseiten setzen alle noch auf die Version 3 auf), und dafür wiederum von &lt;em&gt;LESS&lt;/em&gt; auf &lt;em&gt;SASS&lt;/em&gt; wechseln. Der wiederum nächste Schritt wäre dann ein PHP-basiertes Templating-System, und dann musste all das zusammengesetzt und um die notwendige Funktionalität erweitert werden.&lt;/p&gt;

&lt;h3 id=&quot;-bootstrap-5-und-sass-&quot;&gt;&lt;em&gt;Bootstrap 5&lt;/em&gt; und &lt;em&gt;SASS&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Bootstrap 5&lt;/em&gt; erwies sich als gar nicht so anders; klar, das Grid ist neu und setzt auf Flexbox, es gibt einen Haufen Utility-Klassen für Abstände und bei den Komponenten hat das neue &amp;#8220;Card&amp;#8221;-Element einige bisherige Elemente ersetzt, aber die einzige Neuerung mit Umstellungs- und Änderungsbedarf war der Wechsel von &lt;em&gt;LESS&lt;/em&gt; als CSS-Preporzessor zu &lt;em&gt;SASS&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ich habe also zunächst die &lt;a href=&quot;https://getbootstrap.com/docs/5.0/getting-started/download/#source-files&quot; title=&quot;Download · Bootstrap v5.0&quot;&gt;Quell-Dateien&lt;/a&gt; heruntergeladen und mich damit vertraut gemacht und mir dann eines der &lt;a href=&quot;https://getbootstrap.com/docs/5.0/examples/&quot; title=&quot;Examples · Bootstrap v5.0&quot;&gt;Beispiele&lt;/a&gt; ausgesucht, um davon ausgehend einen groben Design-Entwurf zu puzzeln. Das war dann auch der Zeitpunkt, wo ich einen &lt;em&gt;SASS&lt;/em&gt;-Compiler brauchte. Das erwies sich als überraschend einfach: &lt;a href=&quot;https://sass-lang.com/dart-sass&quot; title=&quot;Document moved&quot;&gt;Dart Sass&lt;/a&gt; bietet Pakete für Windows an. Herunterladen, &lt;code&gt;dart.exe&lt;/code&gt; und &lt;code&gt;sass.snapshot&lt;/code&gt; in ein &lt;code&gt;bin/&lt;/code&gt;-Verzeichnis packen, eine Batch-Datei mit &lt;code&gt;bin/dart.exe bin/sass.snapshot mein.scss mein.css&lt;/code&gt; ins Hauptverzeichnis des Projekts, und schon kann ich aus &lt;code&gt;mein.scss&lt;/code&gt; eine CSS-Datei zaubern. In der &lt;code&gt;.scss&lt;/code&gt;-Datei importiere ich dann die notwendigen &lt;em&gt;Bootstrap&lt;/em&gt;-Komponenten (oder auch einfach alles), ggf. weitere Elemente (wie &lt;em&gt;Font Awesome&lt;/em&gt;), passe Variablen an (insbesondere das Farbschema), ergänze eigenes CSS - et voilá. Und all das kann ich dann direkt lokal auf meinem unter Windows laufenden Entwicklungsrechner testen: Änderungen vornehmen, Build-Script starten, HTML-Datei im Browser neu laden - und ggf. von vorne.&lt;/p&gt;

&lt;p&gt;Eine solche &lt;code&gt;.scss&lt;/code&gt;-Datei kann dann beispielsweise so anfangen:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/* --------------------------------------------------------
 *  SCSS styles and Bootstrap import
 */

// custom variables
$primary:   #314e8c;
$secondary: #bad1e1;
$info:      #ced4da; /* gray-400 */
$body-bg:   $secondary;

// import from Bootstrap
@import &quot;bootstrap/scss/bootstrap.scss&quot;;

// import from FontAwesome (Solid)
@import &quot;fontawesome/scss/fontawesome.scss&quot;;
@import &quot;fontawesome/scss/solid.scss&quot;;

// custom SCSS
/* --------------------------------------------------------
 * Text elements
 */

h1, h2, h3, h4, h5, h6 {
  color: $primary;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Damit werden die gewünschten Primär- und Sekundärfarben gesetzt, die ich aus dem bisherigen Design übernommen habe, die Farbe für &amp;#8220;Info&amp;#8221;-Boxen auf einen Grauton umgestellt und der Hintergrund in der Sekundärfarbe eingefärbt. Dann ziehe ich das komplette &lt;em&gt;Bootstrap&lt;/em&gt;-Framework und &lt;em&gt;Font Awesome&lt;/em&gt; als Webfont ein (hier kann man sich natürlich auf die Elemente beschränken, die man nutzt; das macht alles schlanker), und danach folgen die Anpassungen und eigenen Elemente. So färbe ich bspw. die Überschriften gerne in der Primärfarbe der jeweiligen Website ein.&lt;/p&gt;

&lt;p&gt;Das war, ehrlich gesagt, im Ergebnis dann sehr viel einfacher als ich befürchtet habe, und es gelang mir überraschend schnell, ein brauchbares Mockup zu erstellen. Feinheiten folgen dann, wie immer, sobald sie für bestimmte Elemente oder Situationen benötigt werden.&lt;/p&gt;

&lt;p&gt;Den ganzen Kram - &lt;em&gt;Bootstrap&lt;/em&gt;-Quellcode, &lt;em&gt;Font-Awesome&lt;/em&gt;-Quellcode, die ausführbaren Dateien von &lt;em&gt;Dart Sass&lt;/em&gt; und meine &lt;code&gt;.scss&lt;/code&gt;-Datei - habe ich natürlich in ein Git-Repository gepackt. Dabei haben sowohl &lt;em&gt;Bootstrap&lt;/em&gt; als auch &lt;em&gt;Font Awesome&lt;/em&gt; als auch &lt;em&gt;Dart Sass&lt;/em&gt; jeweils nach dem initialen Einkopieren der Dateien einen eigenen Branch erhalten. Wenn ich nun bspw. eine neue &lt;em&gt;Bootstrap&lt;/em&gt;-Version einspielen will, genügt es, den Bootstrap-Branch auszuchecken, die neuen Dateien einzukopieren und ihn in den Master-Branch zu mergen. So sehe ich sofort alle Änderungen und kann ggf. Anpassungen vornehmen.&lt;/p&gt;

&lt;p&gt;Die am Ende dieser Operationen erzeugte &lt;code&gt;mein.css&lt;/code&gt;-Datei kann ich dann jeweils in das Repository (oder die Repositories) für den Quellcode der einzelnen Webseiten einkopieren.&lt;/p&gt;

&lt;h3 id=&quot;templates&quot;&gt;Templates&lt;/h3&gt;

&lt;p&gt;So weit, so gut. Im nächsten Schritt sollte ich dann aus dem Mockup ein Template machen, in das sich Inhalte einbinden lassen. Davor bedarf es aber eines Template-Systems, das diesen Schritt (Template laden, mit Inhalten füllen) technisch umsetzt.&lt;/p&gt;

&lt;p&gt;Für eine simple interne Seite - Erzeugen von Mailboxen und Mailadressen über ein Webinterface - hatte ich 2017 bei der Renovierung des ursprünglich aus dem Jahr 2009 stammenden &lt;a href=&quot;https://netz-rettung-recht.de/archives/1475-Personalisierte-Mailadressen.html&quot; title=&quot;&quot;&gt;Codes&lt;/a&gt; auf &lt;a href=&quot;http://platesphp.com/&quot; title=&quot;Plates - Native PHP Templates&quot;&gt;&lt;em&gt;Plates&lt;/em&gt;&lt;/a&gt;, ein einfaches PHP-Templating-System, gesetzt. Zusammen mit &lt;em&gt;Bootstrap&lt;/em&gt; sah das hübsch aus und war funktional, wenn auch vielleicht von der Funktionalität her eher überschaubar. So richtig wollte das aber nichts werden; schon das Einbinden machte mir Schwierigkeiten, und mit dem Templating-System wurde ich ebenfalls nicht warm.&lt;/p&gt;

&lt;p&gt;Also dachte ich mir, warum nicht etwas verwenden, von dem ich weiß, dass es sich anderswo bereits bewährt hat? So habe ich mich dann für &lt;a href=&quot;https://www.smarty.net/&quot; title=&quot;PHP Template Engine | Smarty&quot;&gt;&lt;em&gt;Smarty&lt;/em&gt;&lt;/a&gt; entschieden, denn was für &lt;em&gt;Serendipity&lt;/em&gt; gut ist, kann für ein simples Projekt nicht schlecht sein. Außerdem kann ich dann das, was ich lerne, direkt noch anderswo zum Einsatz bringen. Das funktionierte dann auch direkt sehr schön, zumal &lt;em&gt;Smarty&lt;/em&gt; das Einbinden von Teil-Templates (Partials) in Templates unterstützt; man kann sich also das endgültige Template aus wiederverwertbaren Bausteinen zusammenstückeln. Einen Wermutstropfen gibt es freilich: die Templates sind kein &lt;em&gt;HAML&lt;/em&gt;, sondern HTML und &lt;em&gt;Smarty&lt;/em&gt;, aber das passt - so viele Templates braucht man ja in der Regel nicht.&lt;/p&gt;

&lt;p&gt;Wichtiger war mir, die Inhalte nicht komplett als HTML verfassen zu müssen; &lt;em&gt;Markdown&lt;/em&gt; ist da meine Standardlösung. Und wenn ich mich schon an &lt;em&gt;Serendipity&lt;/em&gt; bediene, warum dann nicht auch hier? Das &lt;em&gt;Markdown&lt;/em&gt;-Plugin dort nutzt &lt;a href=&quot;https://michelf.ca/projects/php-markdown/&quot; title=&quot;PHP Markdown&quot;&gt;PHP Markdown&lt;/a&gt;, und das lässt sich natürlich auch sehr schön in meinem Projekt verwenden.&lt;/p&gt;

&lt;h3 id=&quot;model-view-controller&quot;&gt;Model-View-Controller&lt;/h3&gt;

&lt;p&gt;Jetzt mussten alle diese Einzelteile &amp;#8220;nur noch&amp;#8221; sinnvoll zusammengeführt werden. In meinem schon angesprochenen &lt;em&gt;Plates&lt;/em&gt;-Projekt hatte ich für jede Webseite weiterhin jeweils eine gesonderte PHP-Datei, die im Prinzip aber nichts anderes machte, als das Template aufzurufen und mit Inhalten zu füllen. Außerdem gab es für jede Seite dann ein gesondertes Template, dass auf ein Standard-Template aufsetzte. Das kann aber natürlich nicht die Lösung sein. Nicht nur &lt;em&gt;Serendipity&lt;/em&gt; macht das anders: im Prinzip gibt es primär eine PHP-Datei, nämlich eine &lt;code&gt;index.php&lt;/code&gt; im Webroot, die als Parameter die jeweils anzuzeigende Webseite übergeben bekommt und sich dann um den Rest kümmert.&lt;/p&gt;

&lt;p&gt;Im Grundsatz ist das ein &lt;a href=&quot;https://de.wikipedia.org/wiki/Model_View_Controller&quot; title=&quot;&quot;&gt;&lt;em&gt;Model-View-Controller&lt;/em&gt;&lt;/a&gt;-Pattern, kurz &lt;em&gt;MVC&lt;/em&gt;. Das &lt;em&gt;Model&lt;/em&gt; liefert die Daten, sorgt also in einer Applikation üblicherweise für die Abbildung in einer Datenbank. &lt;em&gt;Views&lt;/em&gt; sind die Präsentation, die Templates, die mit Inhalten gefüllt werden. Und der &lt;em&gt;Controller&lt;/em&gt; führt beides zusammen. Natürlich kann man das mehr oder weniger ausgefeilt betreiben; mir genügte eine weniger ausgefeilte Lösung, die bspw. einzelne Aktionen im &lt;em&gt;Controller&lt;/em&gt; nicht in Funktionen auslagert, sondern im Kern auf ein langes &lt;code&gt;switch&lt;/code&gt;-Statement setzt. Immerhin enthält der Großteil der Webseiten primär einmal Text, der angezeigt werden muss; ich will ja keine Webapp schreiben.&lt;/p&gt;

&lt;p&gt;Am Anfang steht die Frage, wie man die anzuzeigende Webseite als Parameter übergeben möchte. Eine URL wie &lt;code&gt;https://meine-website.example/index.php?page=about/contact&lt;/code&gt; sieht ja wirklich hässlich aus und ist auch unter SEO-Gesichtspunkten keine gute Idee. Stattdessen möchte ich gerne URLS wie &lt;code&gt;https://meine-website.example/about/contact/&lt;/code&gt; haben. Hier kommt &lt;em&gt;mod_rewrite&lt;/em&gt; zu Hilfe:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;RewriteEngine On
# rewrite URLs to controller
RewriteCond %{REQUEST_URI} !^/.well-known
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php?page=$1 [QSA,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Die erste der beiden Bedingungen sorgt dafür, dass alle Aufrufe in das Verzeichnis &lt;code&gt;/.well-known/&lt;/code&gt; nicht umgesetzt werden; das ist bspw. erforderlich, damit &lt;code&gt;certbot&lt;/code&gt; von &lt;em&gt;Let&amp;#8217;s encrypt&lt;/em&gt; seine Aufgaben erfüllen kann. Die zweite sorgt dafür, dass tatsächlich physisch vorhandene Dateien angezeigt werden. So lassen sich bspw. Textdateien oder Downloads unterbringen, die &amp;#8220;as is&amp;#8221; ausgeliefert werden sollen. Die darauffolgende Regel schnappt sich den Pfad - im Beispiel also &lt;code&gt;about/contact/&lt;/code&gt; - und ruft die &lt;code&gt;index.php&lt;/code&gt; mit diesem Pfad als Parameter auf. Damit lässt sich arbeiten.&lt;/p&gt;

&lt;p&gt;Im zentralen PHP-Script wird dieser Pfad dann zunächst normalisiert (damit er mit oder ohne abschließenden Slash &amp;#8220;/&amp;#8221; zur selben Seite führt) und auf unerwünschte Zeichen geprüft, damit nicht böse Menschen versuchen, unerwünschte Pfade (&lt;code&gt;../../../../etc/passwd&lt;/code&gt;) zu übergeben. Der Rest des Scripts arbeitet dann mit diesem Pfad weiter. Dazu wird eine weitgehend aus &lt;em&gt;Smarty&lt;/em&gt; abgeleitete Controller-Klasse benutzt, die ein Template-Objekt erzeugt und mit Variablen befüllt; wo erforderlich, steht eine spezialisierte Datenbank-Klasse bereit, die spezifische Funktionen für alle erforderlichen Lese- und Schreib-Operationen bereithält. Für das Session-Management konnte ich auf eine bestehende PHP-Klasse aus dem Jahre 2011 zurückgreifen, die ich damals für ein anderes Projekt geschrieben und bewusst modular aufgebaut hatte; tatsächlich war sie 10 Jahre später mit kleinen Anpassungen - vor allem in bisher nicht oder kaum genutzten Codepfaden - sofort verwendbar. Auth- und Controller-Klasse lassen sich modular wiederverwenden, die Datenbank-Klasse ist weitgehend projektspezifisch.&lt;/p&gt;

&lt;p&gt;Den Hauptteil der &lt;code&gt;index.php&lt;/code&gt; bildet dann tatsächlich ein langes &lt;code&gt;switch&lt;/code&gt;-Statement, das für bestimmte Seiten spezifischen (Datenbank-)Code enthält, in den meisten Fällen aber auf &lt;code&gt;default&lt;/code&gt; zurückfallen und schlicht eine &lt;em&gt;Markdown&lt;/em&gt;-Datei in HTML konvertieren, in ein Template einsetzen und dann anzeigen kann.&lt;/p&gt;

&lt;h3 id=&quot;verzeichnisstruktur&quot;&gt;Verzeichnisstruktur&lt;/h3&gt;

&lt;p&gt;Die Verzeichnisstruktur des Projekts sieht im Grundsatz so aus:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;\___ source/
 \
  \__ data/
   \
    \__ web/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Das &lt;code&gt;web&lt;/code&gt;-Verzeichnis ist das Webroot; dort finden sich die &lt;code&gt;index.php&lt;/code&gt;, die Unterverzeichniss mit CSS, Javascript, Webfonts, Bildern und Icons und die Downloadverzeichnisse, also alles, was &amp;#8220;as is&amp;#8221; ausgeliefert werden soll.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;source&lt;/code&gt; liegen die Module und externen Elemente: &lt;em&gt;Smarty&lt;/em&gt;, &lt;em&gt;PHP Markdown Extra&lt;/em&gt;, meine eigenen Module und eine Konfigurationsdatei, die etliche Variablen setzt (bspw. für die Datenbankanbindung) und zugleich alle notwendigen Elemente einbindet.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;data&lt;/code&gt; schließlich enthält zum einen die Templates (also die typischen &lt;em&gt;Smarty&lt;/em&gt;-Verzeichnisse wie &lt;code&gt;templates&lt;/code&gt;, &lt;code&gt;templates_c&lt;/code&gt;, &lt;code&gt;cache&lt;/code&gt; und Co.) und zum anderen im Verzeichnis &lt;code&gt;content&lt;/code&gt; die eigentlichen Inhalte in Form von &lt;em&gt;Markdown&lt;/em&gt;-Dateien in einer Verzeichnisstruktur, die der der Webseite entspricht. Der Inhalt, der nach Aufruf von &lt;code&gt;https://meine-website.example/about/contact/&lt;/code&gt; angezeigt werden soll, liegt also in &lt;code&gt;data/content/about/contact.md&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;umsetzungsbeispiele&quot;&gt;Umsetzungsbeispiele&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;$controller = new Controller($config);
$controller-&amp;gt;assign(&#039;page&#039;,$page);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Auf diese Weise wird ein neues Controller-Element erzeugt, dass den Inhalt des Arrays &lt;code&gt;$config&lt;/code&gt; (aus der zentralen Konfigurationsdatei) und die anzuzeigende Seite (&lt;code&gt;$page&lt;/code&gt;) in &lt;em&gt;Smarty&lt;/em&gt; verfügbar macht. Die &lt;code&gt;assign&lt;/code&gt;-Methode akzeptiert entweder ein Array oder - wie im Beispiel - ein Tupel aus Schlüssel und Wert.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# create pages from Markdown/YAML
$yaml = $controller-&amp;gt;create_from_md($page);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Diese Funktion liest eine Datei ein, konvertiert das enthaltene &lt;em&gt;Markdown&lt;/em&gt; in HTML und kann &lt;em&gt;YAML front matter&lt;/em&gt; verarbeiten, also &lt;a href=&quot;https://yaml.org/&quot; title=&quot;  - YAML Ain&#039;t Markup Language&quot;&gt;&lt;em&gt;YAML&lt;/em&gt;&lt;/a&gt;, das als Vorspann (eingefasst in &lt;code&gt;---&lt;/code&gt;) in einer anderen Datei enthalten ist. Diese Inhalt werden in &lt;em&gt;Smarty&lt;/em&gt; als Variablen bereitgestellt.&lt;/p&gt;

&lt;p&gt;Die technische Umsetzung der Funktion &lt;code&gt;create_from_md()&lt;/code&gt; ist im Kern wirklich einfach:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# parse YAML and copy to Smarty
$yaml = yaml_parse_file($content_file);
$this-&amp;gt;template-&amp;gt;assign($yaml);

# parse Markdown, ignoring YAML front matter, and assign to content
$this-&amp;gt;template-&amp;gt;assign(&#039;content&#039;,Michelf\MarkdownExtra::defaultTransform($this-&amp;gt;read_ignore_frontmatter($content_file)));

# return YAML metadate
return $yaml;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Die PHP-Funktion &lt;code&gt;yaml_parse_file()&lt;/code&gt; kann &lt;em&gt;YAML front matter&lt;/em&gt; verarbeiten und ignoriert den Rest. Die selbstgeschriebene Funktion &lt;code&gt;read_ignore_frontmatter()&lt;/code&gt; ist das Gegenstück dazu; sie sorgt dafür, dass beim erneuten Einlesen der Datei diesmal das &lt;em&gt;YAML front matter&lt;/em&gt; übersprungen und nur der &lt;em&gt;Markdown&lt;/em&gt;-Teil verarbeitet wird. Danach werden die eingelesenen &lt;em&gt;YAML&lt;/em&gt;-Daten zurückgegeben und stehen daher nicht nur im &lt;em&gt;Smarty&lt;/em&gt;-Template, sondern auch für den PHP-Code zur Verfügung.&lt;/p&gt;

&lt;p&gt;Auf diese Weise können beispielsweise Titel und Beschreibung der Seite, der Autor usw. übergeben werden. Für die (wenigen) Seiten, die nicht nur aus &lt;em&gt;Markdown&lt;/em&gt; bestehen, sondern weitergehende Funktionalität enthalten, werden diese Daten als Array in der &lt;code&gt;index.php&lt;/code&gt; definiert. Jede Webseite besteht also entweder aus einem speziellen &lt;em&gt;Smarty&lt;/em&gt;-Template mit zusätzlichem Code in der &lt;code&gt;index.php&lt;/code&gt; oder aus dem Inhalt einer &lt;em&gt;Markdown&lt;/em&gt;-Datei, die in das Default-Template eingesetzt wird.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# check for protected pages
if(isset($yaml[&#039;login&#039;])) $auth-&amp;gt;check_session();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wenn im &lt;em&gt;YAML&lt;/em&gt;-Vorspann &lt;code&gt;login&lt;/code&gt; gesetzt ist, wird geprüft, ob ein berechtigter Benutzer eingeloggt ist; ansonsten erfolgt die Umleitung zur Anmeldeseite.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# display template
$controller-&amp;gt;display(&#039;index.tpl&#039;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Am Schluss wird das mit Inhalt gefüllte Template angezeigt.&lt;/p&gt;

&lt;h3 id=&quot;ein-paar-worte-zum-schluss&quot;&gt;Ein paar Worte zum Schluss&lt;/h3&gt;

&lt;p&gt;Das Konzept stand natürlich nicht von Anfang an in der hier präsentierten Form, sondern ist dynamisch gewachsen - so gab es zunächst nur &lt;em&gt;Smarty&lt;/em&gt;-Templates, dann kam die Möglichkeit von &lt;em&gt;Markdown&lt;/em&gt;-Dateien hinzu, und noch später das Parsing von &lt;em&gt;YAML front matter&lt;/em&gt;. Und natürlich waren viele Tests und etliche Iterationen über einige Abende und ein langes Wochenende notwendig - aber am Ende war ich überrascht, erfreut und beeindruckt, mit wie vergleichsweise geringem Aufwand sich ein solches doch (für meine Verhältnisse) einigermaßen komplexes Projekt umsetzen lässt. Frei verfügbare externe Projekte und Frameworks - &lt;em&gt;Smarty&lt;/em&gt;, &lt;em&gt;PHP Markdown&lt;/em&gt;, &lt;em&gt;Bootstrap&lt;/em&gt;, &lt;em&gt;SASS&lt;/em&gt; und natürlich am Ende auch &lt;em&gt;PHP&lt;/em&gt; selbst -, eigene kleine Module und ein bißchen Klebstoff, um das alles miteinander zu verbinden, dazu ein altes Design zum Aufhübschen und die bestehenden Inhalte, die ergänzt und übernommen werden, sind eine ebenso mächtige wie vielseitige Kombination.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Nachträglich veröffentlicht im August 2021.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Titelbild © pripir - stock.adobe.com&lt;/em&gt;&lt;/p&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/1f21a4874f62403e99eb8b517e2fa29e&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Thu, 25 Mar 2021 21:20:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/2304-guid.html</guid>
    <category>bootstrap</category>
<category>php</category>
<category>webdesign</category>

</item>
<item>
    <title>Webserver-Tuning</title>
    <link>https://netz-rettung-recht.de/archives/2021-Webserver-Tuning.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/2021-Webserver-Tuning.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=2021</wfw:comment>

    <slash:comments>11</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=2021</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Alle paar Monate wieder turnt ein - weniger rücksichtsvoller - Crawler vorbei und spidert sich zügig durch alle Einträge meines Blogs. Damit brachte er meinen bisherigen Server gerne an den Rand seiner Leistungsfähigkeit, durfte dieser doch für jeden Link einen &lt;code&gt;php-cgi&lt;/code&gt;-Prozess starten. Man merkte das recht schnell an den steigenden Antwortzeiten, und auch interaktiv auf der Shell machte sich die steigende &lt;em&gt;load&lt;/em&gt; bemerkbar. Am Ende blieben meist einige &lt;code&gt;php-cgi&lt;/code&gt;-Prozesse übrig, die man dann manuell mittels &lt;code&gt;kill&lt;/code&gt; entsorgen durfte. (Vielleicht lag auch hier das eigentliche Problem, dass nämlich die genutzten Ressourcen nicht wieder freigegeben wurden. Hinter die Einzelheiten bin ich nie so recht gekommen.)&lt;/p&gt;

&lt;p&gt;Die brandneue Maschine hingegen sollte durch &lt;em&gt;FastCGI&lt;/em&gt; und massenhaft RAM sowie schnelle SSDs einem solchen Ansturm (auch bei weiterer Verwendung des doch eher schwergewichtigen Apachen) besser gewachsen sein - dachte ich mir. Bis eines Freitagmorgens die E-Mails des Monitoring-System eingingen, die mir mitteilten, dass der Webserver nicht mehr auf Anfragen reagiert. Gar nicht mehr.&lt;/p&gt;

&lt;p&gt;Der Grund dafür fand sich schnell im Log:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[mpm_event:error] [pid 1365:tid 139856442496192] AH00484: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Alle Threads des Webservers waren mit Anfragen ausgelastet (und offensichtlich wurden sie auch nicht mehr freigegeben - oder der Apache hatte sich &amp;#8220;verschluckt&amp;#8221;). Ein &lt;kbd&gt;service apache2 restart&lt;/kbd&gt; löste daher das Problem; Zeit aber für einen Blick in die Konfiguration - vielleicht sollte man etwas an den Limits drehen (&amp;#8220;consider raising the MaxRequestWorkers setting
&amp;#8220;).&lt;/p&gt;

&lt;h3 id=&quot;apache-event-mpm&quot;&gt;Apache Event-MPM&lt;/h3&gt;

&lt;p&gt;Für das verwendete &lt;em&gt;MPM&lt;/em&gt; findet sich die entsprechende Konfiguration (unter Debian) in &lt;code&gt;/etc/apache2/mods-enabled/mpm_event.conf&lt;/code&gt;; sie sieht per default so aus:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# event MPM
# StartServers: initial number of server processes to start
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestWorkers: maximum number of worker threads
# MaxConnectionsPerChild: maximum number of requests a server process serves
&amp;lt;IfModule mpm_event_module&amp;gt;
       StartServers              2
       MinSpareThreads          25
       MaxSpareThreads          75
       ThreadLimit              64
       ThreadsPerChild          25
       MaxRequestWorkers       150
       MaxConnectionsPerChild    0
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Es werden also standardmäßig zwei Serverprozesse (&lt;code&gt;StartServers&lt;/code&gt;)gestartet; jeder hat 25 Threads (&lt;code&gt;ThreadsPerChild&lt;/code&gt;), also können 50 parallele Anfragen abgearbeitet werden. Sobald weniger als 25 freie Threads zur Verfügung stehen (&lt;code&gt;MinSpareThreads&lt;/code&gt;), werden weitere Serverprozesse gestartet, bis zum Maximum von 150 Threads (&lt;code&gt;MaxRequestWorkers&lt;/code&gt;) - das entspricht sechs Prozessen (mit je 25 Threads). Sobald mehr als 75 Threads unbeschäftigt sind (&lt;code&gt;MaxSpareThreads&lt;/code&gt;), werden Serverprozesse wieder zurückgefahren.&lt;/p&gt;

&lt;p&gt;150 Prozesse für eine Vielzahl von Webpräsenzen erschien mir wirklich nicht viel; ich habe (angesichts des zur Verfügung stehenden Speichers) daher etwas nachgelegt:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mpm_event_module&amp;gt;
        ServerLimit               40
        StartServers               5
        MinSpareThreads          100
        MaxSpareThreads          250
        ThreadLimit               64     
        ThreadsPerChild           25
        MaxRequestWorkers       1000
        MaxConnectionsPerChild  1000
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wir fangen jetzt also mit mit 125 Threads an (fünf Server mit je 25 Threads) und halten minimal 100 Threads bereit, maximal 250 - eine Lastspitze kann daher besser abgefedert werden, weil von Anfang an mehr Ressourcen bereitstehen und schneller zusätzliche Ressourcen hochgefahren werden). Maximal können dann 1.000 Threads laufen (statt vorher 150) - zu diesem Zweck muss auch &lt;code&gt;ServerLimit&lt;/code&gt; erhöht werden, das per Default auf 16 steht - denn 1.000 Threads brauchen bei 25 Threads pro Prozess 40 Prozesse (mit dem Default von 16 Prozessen kommt man daher maximal auf 400 Threads).&lt;/p&gt;

&lt;p&gt;Außerdem lasse icn jeden Thread nach 1.000 beantworteten Anfragen neu starten (&lt;code&gt;MaxConnectionsPerChild&lt;/code&gt;); so können sich Speicherlecks nicht zu Problemen auswachsen.&lt;/p&gt;

&lt;p&gt;Seitdem war der Webserver jedenfalls nicht mehr komplett ausgelastet; schauen wir mal, ob das schon der richtige Mittelweg zwischen der Bereitstellung ausreichender Ressourcen und dem Schutz des Gesamtsystems vor Überlast war oder mir als nächstes die ganze Maschine in die Knie geht, wenn es mal richtig rundgeht &amp;#8230;&lt;/p&gt;

&lt;h3 id=&quot;fpm-konfiguration&quot;&gt;FPM-Konfiguration&lt;/h3&gt;

&lt;p&gt;Für Webapplikationen in PHP ist die Apache-Konfiguration nicht das einzige limitierende Element - vielmehr muss jede Anfrage auch von einem &lt;em&gt;FastCGI&lt;/em&gt;-Prozess beantwortet werden, und auch die sind limitiert: per Default (bei Debian) auf 50 pro Pool, konfiguriert (unter Strech) in &lt;code&gt;/etc/php/7.0/fpm/pool.d/www.conf&lt;/code&gt;, wobei statt &lt;code&gt;www.conf&lt;/code&gt; eben der jeweilige Pool einzusetzen ist.&lt;/p&gt;

&lt;p&gt;Das erscheint mir ebenfalls nicht besonders viel; ich habe daher den Pool für mein Blog etwas nach oben angepasst:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;pm.max_children = 250
pm.start_servers = 30
pm.min_spare_servers = 10
pm.max_spare_servers = 50
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Zu lesen ist das ähnlich wie oben beim Apachen: 30 Prozesse stehen von Anfang an zur Verfügung, 10&amp;#160;müssen mindestens immer arbeitslos sein, sonst werden Prozesse nachgelegt, bis zur Maximalzahl von 250. Ab 50 &amp;#8220;arbeitslosen&amp;#8221; Prozessen werden überzählige Prozesse gestoppt.&lt;/p&gt;

&lt;h3 id=&quot;erfahrungswerte&quot;&gt;Erfahrungswerte&lt;/h3&gt;

&lt;p&gt;Ich habe bisher, muss ich gestehen, nicht die geringste Erfahrung mit solchen &amp;#8220;Tuning-Aufgaben&amp;#8221;, und werde daher abwarten müssen, ob sich die Werte bewähren. Aus dem Log kann ich jedenfalls sehen, dass 50 Prozesse viel zu wenig waren und auch 150 mehrfach nicht ausreichten:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;WARNING: [pool blog] server reached pm.max_children setting (150), consider raising it
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Andererseits habe ich bisher zumindest aus &lt;em&gt;Munin&lt;/em&gt; - das allerdings nicht sehr fein auflöst - keine Hinweise für Lastspitzen (CPU-Nutzung, RAM-Nutzung und Load); da scheint im Tagesverlauf überall noch viel, viel Luft nach oben zu sein.&lt;/p&gt;

&lt;p&gt;Hat jemand aus der werten Leserschaft Erfahrungen und Tips, die er gerne teilen möchte? Sonst berichte ich, wenn das nächste Mal etwas danebengeht. &lt;img src=&quot;https://netz-rettung-recht.de/plugins/serendipity_event_emoticate/img/emoticons/smile.png&quot; alt=&quot;:-)&quot; class=&quot;emoticon&quot; /&gt;&lt;/p&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/87d9555e0645490ebf5eee951e24a9ff&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Tue, 22 Aug 2017 05:40:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/2021-guid.html</guid>
    <category>apache</category>
<category>debian</category>
<category>followerpower</category>
<category>php</category>
<category>stretch</category>

</item>
<item>
    <title>PHP-FPM - jetzt mit mod_proxy_fcgi</title>
    <link>https://netz-rettung-recht.de/archives/1909-PHP-FPM-jetzt-mit-mod_proxy_fcgi.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/1909-PHP-FPM-jetzt-mit-mod_proxy_fcgi.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=1909</wfw:comment>

    <slash:comments>7</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=1909</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Vergangene Woche hatte ich darüber &lt;a href=&quot;https://netz-rettung-recht.de/archives/1905-PHP-FPM-mit-Debian-Jessie.html&quot; title=&quot;&quot;&gt;berichtet&lt;/a&gt;, wie man - unter &lt;em&gt;Debian Jessie&lt;/em&gt; - &lt;em&gt;PHP-FPM&lt;/em&gt; mit &lt;code&gt;mod_fastcgi&lt;/code&gt; installieren kann. In den &lt;a href=&quot;https://netz-rettung-recht.de/archives/1905-PHP-FPM-mit-Debian-Jessie.html#c4081&quot; title=&quot;&quot;&gt;Kommentaren&lt;/a&gt; hatte &lt;a href=&quot;https://www.svenhartge.de/&quot; title=&quot;&quot;&gt;Sven&lt;/a&gt; mir dann nachfolgend erläutert, wie sich die Einbindung einfacher und besser über &lt;code&gt;mod_proxy_fcgi&lt;/code&gt; lösen lässt, vorausgesetzt, man hat (wie in &lt;em&gt;Debian Jessie&lt;/em&gt;) einen &lt;em&gt;Apache 2.4&lt;/em&gt; vor sich.&lt;/p&gt;

&lt;p&gt;Da mir das ebenfalls vorzugswürdig erscheint, beschreiben ich in der Folge nunmehr diese Variante.&lt;/p&gt;

&lt;h3 id=&quot;-php-fpm-und-mod-proxy-fcgi-installieren&quot;&gt;&lt;em&gt;PHP-FPM&lt;/em&gt; und &lt;code&gt;mod_proxy_fcgi&lt;/code&gt; installieren&lt;/h3&gt;

&lt;p&gt;Unter &lt;em&gt;Debian Jessie&lt;/em&gt; ist die Installation der Pakete hinreichend einfach:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;apt-get install php5-fpm php5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Debian&lt;/em&gt; hinterlegt dabei die &lt;code&gt;php.ini&lt;/code&gt; für die &lt;em&gt;FastCGI&lt;/em&gt;-Prozesse unter &lt;code&gt;/etc/php5/fpm/php.ini&lt;/code&gt; und die Konfiguration für &lt;em&gt;FPM&lt;/em&gt; unter &lt;code&gt;/etc/php5/fpm/php-fpm.conf&lt;/code&gt;, ergänzt u.a. um die Konfiguration installierter Module in &lt;code&gt;/etc/php5/fpm/conf.d/&lt;/code&gt;, wie man das so kennt.&lt;/p&gt;

&lt;p&gt;Im Verzeichnis &lt;code&gt;/etc/php5/fpm/pool.d/&lt;/code&gt; sind dann die einzelnen Pools definiert, die ebenfalls in die &lt;code&gt;/etc/php5/fpm/php-fpm.conf&lt;/code&gt; inkludiert werden, standardmäßig nur der Pool des Benutzers &lt;code&gt;www-data&lt;/code&gt;, unter dem der Webserver läuft.&lt;/p&gt;

&lt;h3 id=&quot;-mod-proxy-fcgi-f-r-php-fpm-konfigurieren&quot;&gt;&lt;code&gt;mod_proxy_fcgi&lt;/code&gt; für &lt;em&gt;PHP-FPM&lt;/em&gt; konfigurieren&lt;/h3&gt;

&lt;p&gt;Serverweit lässt sich &lt;em&gt;PHP-FPM&lt;/em&gt; dann wie folgt in einer neuen Datei &lt;code&gt;/etc/apache2/conf-available/php5-fpm.conf&lt;/code&gt; konfigurieren:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_proxy_fcgi.c&amp;gt;
    &amp;lt;Proxy &quot;unix:/var/run/php5-fpm.sock|fcgi://php-fpm&quot;&amp;gt;
        # we must declare a (any) parameter in here 
        # or it won&#039;t register the proxy ahead of time
        ProxySet disablereuse=off
    &amp;lt;/Proxy&amp;gt;
    &amp;lt;FilesMatch &quot;.+\.php$&quot;&amp;gt;
        SetHandler proxy:fcgi://php-fpm
    &amp;lt;/FilesMatch&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Auf diese Weise werden auf &lt;code&gt;.php&lt;/code&gt; endende Dateien an den darüber definierten Prxoy durchgereicht. Wer will, kann stattdessen bspw. auch &lt;code&gt;&amp;lt;FilesMatch &quot;.+\.ph(p[345]?|t|tml)$&quot;&amp;gt;&lt;/code&gt; verwenden, wie Sven das empfohlen hat; das deckt auch alle anderen gebräuchlichen Endungen für &lt;em&gt;PHP&lt;/em&gt;-Scripts ab.&lt;/p&gt;

&lt;p&gt;Standardmäßig sorgt Debian übrigens dafür, dass das - mitinstallierte - Modul &lt;code&gt;mod_proxy&lt;/code&gt; &lt;em&gt;nicht&lt;/em&gt; als &lt;em&gt;forward proxy&lt;/em&gt; arbeitet; das ist in der Datei &lt;code&gt;/etc/apache2/mods-available/proxy.conf&lt;/code&gt; konfiguriert und stellt also kein Problem dar.&lt;/p&gt;

&lt;p&gt;Nach Aktivieren des Moduls und der Konfiguration vermittels &lt;code&gt;a2enmod proxy_fcgi&lt;/code&gt; und &lt;code&gt;a2enconf php5-fpm&lt;/code&gt; sowie einem Restart des Webservers (&lt;code&gt;service apache2 restart&lt;/code&gt;) sollte PHP nun zur Verfügung stehen.&lt;/p&gt;

&lt;h3 id=&quot;pools-f-r-einzelne-benutzer-einrichten&quot;&gt;Pools für einzelne Benutzer einrichten&lt;/h3&gt;

&lt;p&gt;Um nun verschiedene Pools für verschiedene Benutzer einzurichten, bedarf es neben einer passenden Konfiguration des jeweiligen Pools in &lt;code&gt;/etc/php5/fpm/pool.d/&lt;/code&gt; und eines geänderten Aufrufs in jedem &lt;em&gt;virtual host&lt;/em&gt;, der diesem Benutzer zugeordnet ist.&lt;/p&gt;

&lt;p&gt;Am einfachsten kopiert man sich die bestehende Konfiguration für den Pool &lt;code&gt;www&lt;/code&gt; und wandelt sie ab:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd /etc/php5/fpm/pool.d
cp www.conf user1.conf
vim user1.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Folgende Teile der Konfiguration müssen ersetzt werden: der Name des Pools, &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;group&lt;/code&gt; und &lt;code&gt;listen&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;; Start a new pool named &#039;user1&#039;.
; the variable $pool can we used in any directive and will be replaced by the
; pool name (&#039;user1&#039; here)
[user1]

[...]

user = user1
group = users

[...]

listen = /var/run/php5-fpm-user1.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Selbstverständlich können auch andere, für diesen Pool spezifische Änderungen vorgenommen werden&lt;/p&gt;

&lt;p&gt;Und danach bekommt der bspw. in &lt;code&gt;/etc/apache2/sites-available/user1-domain.example&lt;/code&gt; konfigurierte &lt;em&gt;virtual host&lt;/em&gt; noch folgende Ergänzung (zwischen &lt;code&gt;&amp;lt;VirtualHost ...&amp;gt;&lt;/code&gt; und &lt;code&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_proxy_fcgi.c&amp;gt;
    &amp;lt;Proxy &quot;unix:/var/run/php5-fpm-user1.sock|fcgi://php-fpm-user1&quot;&amp;gt;
        # we must declare a (any) parameter in here 
        # or it won&#039;t register the proxy ahead of time
        ProxySet disablereuse=off
    &amp;lt;/Proxy&amp;gt;
    &amp;lt;FilesMatch &quot;.+\.php$&quot;&amp;gt;
        SetHandler proxy:fcgi://php-fpm-user1
    &amp;lt;/FilesMatch&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In diesem &lt;em&gt;virtual host&lt;/em&gt; kommuniziert der &lt;em&gt;Apache&lt;/em&gt; also nicht mit dem &lt;em&gt;FastCGI&lt;/em&gt;-Server über &lt;code&gt;/var/run/php5-fpm.sock&lt;/code&gt;, sondern über &lt;code&gt;/var/run/php5-fpm-user1.sock&lt;/code&gt;, und den haben wir ja anders konfiguriert. Ein &lt;code&gt;service apache2 restart&lt;/code&gt; bzw. &lt;code&gt;service php5-fpm restart&lt;/code&gt; später sollte die geänderte Konfiguration zur Verfügung stehen.&lt;/p&gt;

&lt;p&gt;Soll PHP in mehreren &lt;em&gt;virtual hosts&lt;/em&gt; unter derselben Benutzerkennung laufen, empfiehlt es sich, die o.g. Konfiguration in eine eigene Datei (bspw. &lt;code&gt;/etc/apache2/sites-available/user1.fcgi&lt;/code&gt;) auszulagern und diese Datei dann in die &lt;em&gt;virtual host&lt;/em&gt;-Konfiguration einzubinden:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Include /etc/apache2/sites-available/user1.fcg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Das spart nicht nur Tipparbeit, sondern erleichtert auch spätere Änderungen sehr.&lt;/p&gt;

&lt;h3 id=&quot;die-installation-testen&quot;&gt;Die Installation testen&lt;/h3&gt;

&lt;p&gt;Am einfachsten gestaltet sich der Test, wenn man im Webroot des Servers (&lt;em&gt;Debian Jessie&lt;/em&gt;: &lt;code&gt;/var/www/html/&lt;/code&gt;)
und der einzelnen &lt;em&gt;virtual hosts&lt;/em&gt; jeweils eine Datei &lt;code&gt;test.php&lt;/code&gt; speichert, die nur &lt;code&gt;phpinfo()&lt;/code&gt; aufruft:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php phpinfo(); ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In der Ausgabe von &lt;code&gt;phpinfo()&lt;/code&gt; wird angezeigt, unter welchem Benutzer das Script ausgeführt wird.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Vielen Dank an Sven für den hilfreichen Tip!&lt;/em&gt;&lt;/p&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/486e1796375b4333b55dd718167e842b&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Tue, 26 Apr 2016 05:50:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/1909-guid.html</guid>
    <category>anleitung</category>
<category>debian</category>
<category>jessie</category>
<category>php</category>

</item>
<item>
    <title>PHP-FPM mit Debian Jessie</title>
    <link>https://netz-rettung-recht.de/archives/1905-PHP-FPM-mit-Debian-Jessie.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/1905-PHP-FPM-mit-Debian-Jessie.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=1905</wfw:comment>

    <slash:comments>6</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=1905</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Statische Webseiten sind nett (und durchaus wieder &lt;a href=&quot;https://netz-rettung-recht.de/archives/1755-Generatoren-fuer-statische-Websites.html&quot; title=&quot;&quot;&gt;im Kommen&lt;/a&gt;), aber zumindest für manche Zwecke sind dynamisch generierte Seiten und die auf diese Weise ermöglichte Interaktivität doch dann besser oder gar notwendig. Facebook, bspw., würde sich als statische Seite dann doch eher schwierig gestalten.&lt;/p&gt;

&lt;h3 id=&quot;die-welt-dynamischer-webseiten&quot;&gt;Die Welt dynamischer Webseiten&lt;/h3&gt;

&lt;h4 id=&quot;scriptsprachen&quot;&gt;Scriptsprachen&lt;/h4&gt;

&lt;p&gt;Dynamisch generierte Seiten bedingen, dass beim Aufruf einer Webseite nicht nur eine auf dem Server abgelegte Datei angezeigt wird, sondern dass ein Programm dort läuft, das die Ausgabe mehr oder weniger live generiert. Das hat ganz andere Implikationen für die Sicherheit des Servers, denn nunmehr läuft dort von außen erreichbarer Code, der schlimmstenfalls noch durch die Nutzer selbst aufgespielt werden kann. Nicht jeder, der seine ersten Gehversuche mit Scripts macht, hat dabei auch Fragen der IT-Sicherheit ausreichend im Blick - um das Problem einmal stark verharmlosend zu beschreiben. Gerade PHP hat nicht den Ruf, zumindest in den ersten Jahren seiner Entwicklung großes Gewicht auf das Fördern oder gar Erzwingen sicherer Praktiken gelegt zu haben, und viele lern(t)en es vor allem als eine Art Templating-Sprache kennen, die man irgendwie in seine HTML-Seiten einbettet, um ihnen damit eine erweiterte Funktionalität zu verleihen, ohne dabei groß an Konsequenzen zu denken (schuldig, euer Ehren!).&lt;/p&gt;

&lt;h4 id=&quot;rechteprobleme&quot;&gt;Rechteprobleme&lt;/h4&gt;

&lt;p&gt;Besondere Schwierigkeiten kommen hinzu, wenn mehr als ein Benutzer auf dem Server Scripts nutzen will. Denn wenn der Webserver bzw. der von diesem gestartete Interpreter das Script ausführen will, muss er es lesen können. Dann muss er aber - logischerweise - auch die Scripts aller anderen nutzer lesen können, was bedeutet, dass Nutzer A ein Sript schreiben kann, mit dem er sich die Scripts von Benutzer B anzeigen lassen kann, samt aller dort gespeicherten Informationen (Datenbankpassworte usw.), ebenso wie Dateien, die Benutzer B anlegt. Und wenn das Script irgendwelche Dateien anlegt (man denke an hochgeladene Bilder), dann werden diese Dateien unter der Nutzerkennung des Webservers angelegt, so dass der Benutzer dann keinen vollen Zugriff darauf hat. Das ist alles etwas unschön, weshalb man schon bald die Möglichkeit geschaffen hat, Scripts (sei es Perl, sei es Python, sei es PHP) unter der Benutzerkennung des jeweiligen Nutzers laufen zu lassen, dem das Script &amp;#8220;gehört&amp;#8221;.&lt;/p&gt;

&lt;h4 id=&quot;-suexec-und-suphp-&quot;&gt;&lt;em&gt;suexec&lt;/em&gt; und &lt;em&gt;suphp&lt;/em&gt;&lt;/h4&gt;

&lt;p&gt;Das schafft zwar andere Probleme, insbesondere im Bereich der Performance, denn nun kann ein Webserverprozess nicht mehr beliebig viele Scripts in parallelen Threads abarbeiten, weil diese ja vielleicht verschiedenen Nutzern gehören, so dass für jedes Script ein neuer Interpreter-Prozess gestartet werden muss, aber es ist doch in der Regel vorzugswürdig.&lt;/p&gt;

&lt;p&gt;Für Scripts, die über die &lt;em&gt;CGI&lt;/em&gt;-Schnittstelle (ja, ich weiß, das &amp;#8220;I&amp;#8221; steht schon für &amp;#8220;Interface&amp;#8221;) gestartet werden, stellt &lt;em&gt;suexec&lt;/em&gt; die entsprechende Funktionalität bereit. Für PHP tat das traditionell &lt;em&gt;suphp&lt;/em&gt;. &lt;em&gt;suphp&lt;/em&gt; gibt es aber in &lt;em&gt;Debian Jessie&lt;/em&gt; nicht mehr. Was also tun?&lt;/p&gt;

&lt;p&gt;Als Lösung bietet sich &lt;strong&gt;&lt;a href=&quot;http://php-fpm.org/&quot; title=&quot;&quot;&gt;PHP-FPM&lt;/a&gt;&lt;/strong&gt; an.&lt;/p&gt;

&lt;h3 id=&quot;-php-fpm-&quot;&gt;&lt;em&gt;PHP-FPM&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;FPM&lt;/em&gt; steht dabei für &lt;em&gt;FastCGI Process Manager&lt;/em&gt; und ist eine sehr angenehme Lösung, um PHP als &lt;em&gt;FastCGI&lt;/em&gt; einzubinden. &lt;em&gt;FastCGI&lt;/em&gt; ist ein Mittelding zwischen der Einbindung per &lt;em&gt;mod_php&lt;/em&gt;, also als Modul im Webserver, und als &lt;em&gt;CGI&lt;/em&gt;, so dass für jeden Aufruf eines PHP-Scripts ein eigener Prozess gestartet werden muss: es werden einige &lt;em&gt;FastCGI&lt;/em&gt;-Prozesse gestartet, die in einem Pool vorgehalten werden, und wenn ein PHP-Script aufgerufen wird, wird der Aufruf vom Webserver an den schon laufenden Prozess weitergereicht. Das erspart den sonst für jeden Aufruf notwendigen Start des Interpreters - und es bietet die Möglichkeit, pro Pool festzulegen, unter welcher Benutzerkennung die Prozesse dieses Pools laufen sollen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hinweis&lt;/strong&gt;: Der folgende Vorschlag ist nicht der einzige Weg, &lt;em&gt;PHP-FPM&lt;/em&gt; zu installieren. Eine &lt;strong&gt;bessere Lösung&lt;/strong&gt; unter Verwendung von &lt;em&gt;mod&amp;#95;proxy&amp;#95;fcgi&lt;/em&gt;, auf die ich in den Kommentaren hingewiesen wurde, habe ich mittlerweile &lt;a href=&quot;https://netz-rettung-recht.de/archives/1909-PHP-FPM-jetzt-mit-mod_proxy_fcgi.html&quot; title=&quot;&quot;&gt;auch beschrieben&lt;/a&gt;. Dieser andere Weg setzt &lt;em&gt;Apache 2.4.x&lt;/em&gt; voraus, benötigt aber kein Paket aus dem nicht-freien &lt;em&gt;Debian&lt;/em&gt;-Repository. &lt;strong&gt;Insgesamt erscheint mir dieser andere Weg vorzugswürdig.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;-php-fpm-installieren&quot;&gt;&lt;em&gt;PHP-FPM&lt;/em&gt; installieren&lt;/h4&gt;

&lt;p&gt;Unter &lt;em&gt;Debian Jessie&lt;/em&gt; ist die Installation der Pakete hinreichend einfach:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;apt-get install libapache2-mod-fastcgi php5-fpm php5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;libapache2-mod-fastcgi&lt;/code&gt; kommt hier aus dem &lt;code&gt;non-free&lt;/code&gt;-Repository, das dementsprechend eingebunden sein muss.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Debian&lt;/em&gt; hinterlegt dabei die &lt;code&gt;php.ini&lt;/code&gt; für die &lt;em&gt;FastCGI&lt;/em&gt;-Prozesse unter &lt;code&gt;/etc/php5/fpm/php.ini&lt;/code&gt; und die Konfiguration für &lt;em&gt;FPM&lt;/em&gt; unter &lt;code&gt;/etc/php5/fpm/php-fpm.conf&lt;/code&gt;, ergänzt u.a. um die Konfiguration installierter Module in &lt;code&gt;/etc/php5/fpm/conf.d/&lt;/code&gt;, wie man das so kennt.&lt;/p&gt;

&lt;p&gt;Im Verzeichnis &lt;code&gt;/etc/php5/fpm/pool.d/&lt;/code&gt; sind dann die einzelnen Pools definiert, die ebenfalls in die &lt;code&gt;/etc/php5/fpm/php-fpm.conf&lt;/code&gt; inkludiert werden, standardmäßig nur der Pool des Benutzers &lt;code&gt;www-data&lt;/code&gt;, unter dem der Webserver läuft.&lt;/p&gt;

&lt;h4 id=&quot;-fastcgi-f-r-php-fpm-konfigurieren&quot;&gt;&lt;em&gt;FastCGI&lt;/em&gt; für &lt;em&gt;PHP-FPM&lt;/em&gt; konfigurieren&lt;/h4&gt;

&lt;p&gt;Wenn man möchte, kann man die bestehende Konfigurationsdatei sichern:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cp /etc/apache2/mods-available/fastcgi.conf /etc/apache2/mods-available/fastcgi.conf.backup
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;und danach sie danach mittels &lt;code&gt;vim /etc/apache2/mods-available/fastcgi.conf&lt;/code&gt; neu befüllen wie folgt:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_fastcgi.c&amp;gt;
    AddType application/x-httpd-fastphp5 .php
    Action application/x-httpd-fastphp5 /php5-fcgi
    Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
    FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -pass-header Authorization
    &amp;lt;Directory /usr/lib/cgi-bin&amp;gt;
        Require all granted
    &amp;lt;/Directory&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Auf diese Weise bekommen auf &lt;code&gt;.php&lt;/code&gt; endende Dateien den Typ &lt;code&gt;x-httpd-fastphp5&lt;/code&gt; zugewiesen, der über den Handler /&lt;code&gt;php5-fcgi&lt;/code&gt; aufgerufen wird, der wiederum auf &lt;code&gt;/usr/lib/cgi-bin/php5-fcgi&lt;/code&gt; zeigt, was aber ebenfalls nicht existiert, so dass &lt;em&gt;Apache&lt;/em&gt; stattdessen alles dem externen &lt;em&gt;FastCGI&lt;/em&gt;-Server zuwirft, der über den Socket &lt;code&gt;/var/run/php5-fpm.sock&lt;/code&gt; kommuniziert.&lt;/p&gt;

&lt;p&gt;Nach Aktivieren der notwendigen Module mittels &lt;code&gt;a2enmod fastcgi actions alias&lt;/code&gt; und einem Restart des Webservers (&lt;code&gt;service apache2 restart&lt;/code&gt;) sollte PHP zur Verfügung stehen.&lt;/p&gt;

&lt;h4 id=&quot;pools-f-r-einzelne-benutzer-einrichten&quot;&gt;Pools für einzelne Benutzer einrichten&lt;/h4&gt;

&lt;p&gt;Um nun verschiedene Pools für verschiedene Benutzer einzurichten, bedarf es neben einer passenden Konfiguration des jeweiligen Pools in &lt;code&gt;/etc/php5/fpm/pool.d/&lt;/code&gt; und eines geänderten Aufrufs in jedem &lt;em&gt;virtual host&lt;/em&gt;, der diesem Benutzer zugeordnet ist.&lt;/p&gt;

&lt;p&gt;Am einfachsten kopiert man sich die bestehende Konfiguration für den Pool &lt;code&gt;www&lt;/code&gt; und wandelt sie ab:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd /etc/php5/fpm/pool.d
cp www.conf user1.conf
vim user1.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Folgende Teile der Konfiguration müssen ersetzt werden: der Name des Pools, &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;group&lt;/code&gt; und &lt;code&gt;listen&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;; Start a new pool named &#039;user1&#039;.
; the variable $pool can we used in any directive and will be replaced by the
; pool name (&#039;user1&#039; here)
[user1]

[...]

user = user1
group = users

[...]

listen = /var/run/php5-fpm-user1.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Selbstverständlich können auch andere, für diesen Pool spezifische Änderungen vorgenommen werden&lt;/p&gt;

&lt;p&gt;Und danach bekommt der bspw. in &lt;code&gt;/etc/apache2/sites-available/user1-domain.example&lt;/code&gt; konfigurierte &lt;em&gt;virtual host&lt;/em&gt; noch folgende Ergänzung (zwischen &lt;code&gt;&amp;lt;VirtualHost ...&amp;gt;&lt;/code&gt; und &lt;code&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_fastcgi.c&amp;gt;
    AddType application/x-httpd-fastphp5 .php
    Action application/x-httpd-fastphp5 /php5-fcgi
    Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi-user1
    FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi-user1 -socket /var/run/php5-fpm-user1.sock -pass-header Authorization
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In diesem &lt;em&gt;virtual host&lt;/em&gt; kommuniziert der &lt;em&gt;Apache&lt;/em&gt; also nicht mit dem &lt;em&gt;FastCGI&lt;/em&gt;-Server über &lt;code&gt;/var/run/php5-fpm.sock&lt;/code&gt;, sondern über &lt;code&gt;/var/run/php5-fpm-user1.sock&lt;/code&gt;, und den haben wir ja anders konfiguriert. Ein &lt;code&gt;service apache2 restart&lt;/code&gt; bzw. &lt;code&gt;service php5-fpm restart&lt;/code&gt; später sollte die geänderte Konfiguration zur Verfügung stehen.&lt;/p&gt;

&lt;p&gt;Soll PHP in mehreren &lt;em&gt;virtual hosts&lt;/em&gt; unter derselben Benutzerkennung laufen, empfiehlt es sich, die o.g. Konfiguration in eine eigene Datei (bspw. &lt;code&gt;/etc/apache2/sites-available/user1.fcgi&lt;/code&gt;) auszulagern und diese Datei dann in die &lt;em&gt;virtual host&lt;/em&gt;-Konfiguration einzubinden:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Include /etc/apache2/sites-available/user1.fcg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Das spart nicht nur Tipparbeit, sondern erleichtert auch spätere Änderungen sehr.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wichtig&lt;/strong&gt;: Der Eintrag &lt;code&gt;FastCgiExternalServer&lt;/code&gt; darf dabei nur einmal erfolgen, also nicht pro &lt;em&gt;vhost&lt;/em&gt; wiederholt werden.&lt;/p&gt;

&lt;h4 id=&quot;die-installation-testen&quot;&gt;Die Installation testen&lt;/h4&gt;

&lt;p&gt;Am einfachsten gestaltet sich der Test, wenn man im Webroot des Servers (&lt;em&gt;Debian Jessie&lt;/em&gt;: &lt;code&gt;/var/www/html/&lt;/code&gt;)
und der einzelnen &lt;em&gt;virtual hosts&lt;/em&gt; jeweils eine Datei &lt;code&gt;test.php&lt;/code&gt; speichert, die nur &lt;code&gt;phpinfo()&lt;/code&gt; aufruft:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php phpinfo(); ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In der Ausgabe von &lt;code&gt;phpinfo()&lt;/code&gt; wird angezeigt, unter welchem Benutzer das Script ausgeführt wird.&lt;/p&gt;

&lt;h3 id=&quot;weiterer-lesestoff&quot;&gt;Weiterer Lesestoff&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Alex Fornuto&lt;/em&gt; bei &lt;em&gt;linode&lt;/em&gt;: &lt;a href=&quot;https://www.linode.com/docs/websites/apache/install-php-fpm-and-apache-on-debian-8&quot; title=&quot;403 Forbidden&quot;&gt;Install PHP-FPM and Apache on Debian 8 (Jessie)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Apache httpd&lt;/em&gt; Wiki: &lt;a href=&quot;http://wiki.apache.org/httpd/PHP-FPM&quot; title=&quot;301 Moved Permanently&quot;&gt;High-performance PHP on apache httpd 2.4.x using mod&amp;#95;proxy&amp;#95;fcgi and php-fpm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/9bcac885cb2d41e483ab77fc7acff8b2&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Mon, 18 Apr 2016 06:45:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/1905-guid.html</guid>
    <category>debian</category>
<category>jessie</category>
<category>php</category>

</item>
<item>
    <title>Digitale Ausstellungsstücke</title>
    <link>https://netz-rettung-recht.de/archives/1648-Digitale-Ausstellungsstuecke.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/1648-Digitale-Ausstellungsstuecke.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=1648</wfw:comment>

    <slash:comments>4</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=1648</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Wer (digitale) Fotos - in welcher Menge, Häufigkeit und Qualität auch immer - macht, der kennt das Problem, sie online verfügbar zu machen. Gut, das eigentliche Problem ist die Überwindung der eigenen Faulheit und das Auswählen, Sortieren, Umbenennen und ggf. Kommentieren einmal wirklich anzugehen, aber man möchte dazu auch die entsprechende, möglichst einfach verwendbare, aber alle notwendigen Funktionen bietende Software haben.&lt;/p&gt;

&lt;p&gt;Man kann dazu Web-2.0-Dienste wie &lt;a href=&quot;http://www.flickr.com/&quot; title=&quot;301 Moved Permanently&quot;&gt;flickr&lt;/a&gt; nutzen, aber nicht jeder möchte das, sei es, daß man solchen Diensten generell nicht traut und/oder die Software lieber selbst installieren möchte, sei es, daß man am &amp;quot;Sharing&amp;quot; von Fotos gar nicht interessiert ist, sondern den Zugriff potentiell nur wenigen ermöglichen möchte. Insoweit habe ich dann schon einiges durch: mehr schlecht als recht selbst gebaute Implementationen für die Webseiten meiner Hilfsorganisation oder einer Newsgroup (seit ~ 2002), dann für meine private Homepage (ebenfalls seit ~ 2002) &lt;em&gt;&lt;a title=&quot;My Image Gallery (mig)&quot; href=&quot;http://mig.sourceforge.net/&quot;&gt;mig&lt;/a&gt;&lt;/em&gt;, ein sehr einfaches Script (auch deshalb, weil man von den Platzhirschen wie Gallery nicht viel Gutes über die Sicherheit hörte), und seit Anfang 2008 (nach dem Erhalt meiner neuen Kamera Weihnachten 2007) auch eine &lt;em&gt;&lt;a href=&quot;http://gallery.menalto.com/&quot; title=&quot;Gallery | Your photos on your website&quot;&gt;Gallery-2&lt;/a&gt;&lt;/em&gt;-Instanz, mit der ich allerdings nie so recht angefreundet hatte (zumal bis zu einem Kurzurlaub 2008 zurück noch Bilder zum Sortieren und Hochladen im Backlog liegen - siehe die einleitenden Worte zu diesem Artikel &lt;img src=&quot;https://netz-rettung-recht.de/plugins/serendipity_event_emoticate/img/emoticons/wink.png&quot; alt=&quot;;-)&quot; class=&quot;emoticon&quot; /&gt;). Während ich jetzt darüber nachdachte, ob sich nicht ein Update auf &lt;em&gt;Gallery 3&lt;/em&gt; anbietet, bekam ich den &lt;a href=&quot;http://www.midnight-visions.de/piwigo/&quot; title=&quot;Midnight Visions, Svens Howto-Blog: piwigo&quot;&gt;weisen&lt;/a&gt; Rat, mir doch einmal &lt;em&gt;piwigo&lt;/em&gt; anzusehen.&lt;/p&gt;

&lt;p&gt;Und das hat mich überzeugt. Zumindest dann, wenn man eine Fotogalerie-Applikation nur für sich selbst ohne ausgefeiltes Benutzermanagement für mehrere Fotografen braucht (und wie oft teilt man sich eine Galerie wirklich zu mehreren?), ist &lt;a href=&quot;http://piwigo.org/&quot; title=&quot;Piwigo - Open source photo management software&quot;&gt;&lt;em&gt;piwigo&lt;/em&gt;&lt;/a&gt; eine tolle Lösung. Installation und Konfiguration sind einfach (Voraussetzungen: PHP und mySQL), wenn man das Prinzip einmal begriffen hat, der Upload ist übers Web wie auch einfach per FTP möglich, Thumbnails werden erstellt, Benutzer können in vorgegebene Rechtegruppen (Gast, Bekannter, Freund, Familie, &amp;#8230;) oder in Nutzergruppen eingeteilt werden, und für Alben bzw. Fotos lassen sich dann die entsprechenden Zugriffsrechte setzen. Fotos lassen sich nicht nur in Alben, sondern in Kategorien zusammenfassen und zugleich noch beliebig taggen; der Benutzer kann sich die Fotos nach Kategorien oder Tags oder nach dem Kalender oder sonstwie anzeigen lassen, bekommt dabei aber immer nur die Fotos oder Kategorien zu sehen, für die er entsprechende Rechte hat. Außerdem gibt es eine Reihe vorgefertigter, recht ansehnlicher Skins.&lt;/p&gt;

&lt;p&gt;Mir gefällt&amp;#8217;s jedenfalls, und ich habe den heutigen Tag dann im wesentlichen dazu genutzt, aus den drei oder vier vorgenannten Quellen der letzten 10 Jahre die dort vorhandenen Fotos in einer Galerie zusammenzuführen und dann den Rest zu löschen. So gesehen also wie gestern ein weiterer Tag des Räumens und Sortierens. &lt;img src=&quot;https://netz-rettung-recht.de/plugins/serendipity_event_emoticate/img/emoticons/smile.png&quot; alt=&quot;:-)&quot; class=&quot;emoticon&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/3b39741bcd8e4c8fb4fbcc23e07ecfc0&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Mon, 10 Jan 2011 18:28:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/1648-guid.html</guid>
    <category>php</category>
<category>piwigo</category>

</item>
<item>
    <title>Mantis Bug Tracker und Mantis Graphs 1.0</title>
    <link>https://netz-rettung-recht.de/archives/1589-Mantis-Bug-Tracker-und-Mantis-Graphs-1.0.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/1589-Mantis-Bug-Tracker-und-Mantis-Graphs-1.0.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=1589</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=1589</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;&lt;a title=&quot;Mantis Bug Tracker&quot; href=&quot;http://www.mantisbt.org/&quot;&gt;Mantis&lt;/a&gt; (aktuell in der Version &lt;strong&gt;1.2.1&lt;/strong&gt;) ist ein ganz netter Bugtracker, also ein Ticketsystem (Fallbearbeitungssystem) für die Softwareentwicklung, dessen auffallendstes Manko allerdings die unterirdische Qualität der Dokumentation (weitgehend schlicht nicht vorhanden, ansonsten grob unvollständig oder weit veraltet) ist. &lt;em&gt;*seufz*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&amp;quot;Mantis Graphs 1.0&amp;quot; ist ein Plugin für Mantis, das graphische Darstellungen ermöglicht, und ich habe heute längere Zeit damit verbracht, es in Betrieb zu setzen, nachdem es immer nur &amp;quot;unable to read/find font&amp;quot; von sich geben wollte. Einer der ersten Schritte war die Installation der msttcorefonts (&lt;em&gt;aptitude install ttf-mscorefonts-installer&lt;/em&gt;), aber das genügte nicht. Längeres Debugging mit eingestreuten Statements im Quellcode ergab schließlich, daß Mantis weder das passende Font-Verzeichnis erkennen noch - bei expliziter Angabe des Verzeichnisses in der Konfiguration mit &lt;em&gt;$g_system_font_folder&lt;/em&gt; - die Fontdatei laden wollte. Die Lösung dafür fand sich in der Dokumentation der PHP-Funktion &lt;em&gt;file_exists()&lt;/em&gt;: diese liefert auch dann &lt;em&gt;false&lt;/em&gt; zurück, wenn auf die entsprechende Datei aufgrund von &lt;a title=&quot;PHP: Security and Safe Mode&quot; href=&quot;http://de.php.net/manual/en/ini.sect.safe-mode.php&quot;&gt;&lt;em&gt;safe_mode&lt;/em&gt;&lt;/a&gt;-Restriktionen nicht zugegriffen werden kann &amp;#8230;&lt;/p&gt;

&lt;p&gt;Und natürlich ist das der Fall: das Font-Verzeichnis - bpw. &lt;em&gt;/usr/share/fonts/truetype/msttcorefonts/&lt;/em&gt; - gehört &lt;em&gt;root&lt;/em&gt;, und die Font-Dateien auch. Jedenfalls ist Owner dieser Dateien niemals der Webserver oder der Benutzer, unter dessen Kennung die PHP-Scripts dank &lt;em&gt;su_php&lt;/em&gt; o.ä. ausgeführt werden. &lt;em&gt;*brummel*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Es bleibt demnach nur der Verzicht auf die Grafiken oder die Deaktivierung von &lt;em&gt;safe_mode&lt;/em&gt; (für Mantis).&lt;br /&gt;&lt;/p&gt;
 
    </content:encoded>

    <pubDate>Fri, 14 May 2010 16:14:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/1589-guid.html</guid>
    <category>debian</category>
<category>lenny</category>
<category>mantis</category>
<category>php</category>

</item>
<item>
    <title>Apache 2.x, php-cgi, mod_suphp und &quot;No input file specified!&quot;</title>
    <link>https://netz-rettung-recht.de/archives/1403-Apache-2.x,-php-cgi,-mod_suphp-und-No-input-file-specified!.html</link>
            <category>Bits'n'Bytes</category>
    
    <comments>https://netz-rettung-recht.de/archives/1403-Apache-2.x,-php-cgi,-mod_suphp-und-No-input-file-specified!.html#comments</comments>
    <wfw:comment>https://netz-rettung-recht.de/wfwcomment.php?cid=1403</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>https://netz-rettung-recht.de/rss.php?version=2.0&amp;type=comments&amp;cid=1403</wfw:commentRss>
    

    <author>nospam@example.com (Thomas Hochstein)</author>
    <content:encoded>
    &lt;p&gt;Im Zusammenhang mit dem Update einer Maschine auf Debian Lenny bin ich auf das seltsame Phänomen einer - offensichtlich von PHP erzeugten - Fehlermedlung gestoßen: der Aufruf einer bestimmten Webseite (respektive des PHP-Scripts, das diese erzeugen sollte) ergibt nur die Meldung &amp;quot;No input file specified!&amp;quot;. Google führt einen zu diversen Threads, die sich vor allem um die Konfiguration des richtigen Handlers für die Interpretation von PHP-Scripts und um hilflose Fragen von Benutzern, die eigentlich nur ein fertiges Paket installieren wollten und nun nicht mehr weiter wissen, drehen. Erst ein alter Blogeintrag in &lt;a href=&quot;http://blog.vodkamelone.de/archives/40-No-input-file-specified-reloaded-oder-komische-Fehlermeldungen-und-noch-komischere-Ursachen.html&quot; title=&quot;&amp;quot;No input file specified&amp;quot; reloaded oder komische Fehlermeldungen und noch komischere Ursachen  - ixs&#039; Vodkamelone&quot;&gt;ixs&amp;#8217; Vodkamelone&lt;/a&gt; (nein, das hat nichts mit einem Kamel Nr. Eins zu tun!) von 2005 half mir auf die Sprünge.&lt;/p&gt;

&lt;p&gt;Der Grund dieser Fehlermeldung ist offenbar ein ganz seltsames Zusammenspiel der Ausführung von PHP als &lt;em&gt;mod_suphp&lt;/em&gt;, d.h. der CGI-Version von PHP in der Weise, daß Scripts unter den Rechten des jeweiligen Benutzers ausgeführt werden, mit der Vergabe von Datei- und Verzeichnisrechten. Die oben genannten Fehlermeldung tritt demnach genau dann auf, wenn zwar der Webserver auf die Datei bzw. ihr Verzeichnis zugreifen, sie also &amp;quot;finden&amp;quot; kann, der Benutzer selbst aber nicht. Dann findet der Webserver - unter der UID &lt;em&gt;www-data&lt;/em&gt; - das Script, erkennt es, ruft den Handler auf, der als suid root installiert ist, sich Root-Rechte verschafft, damit auf die UID des Benutzers wechselt und dann mit diesen Rechten den PHP-Interpreter aufruft, um ihn das Script ausführen zu lassen. Dieser PHP-Prozess, der jetzt aber im Kontext des Benutzers läuft, kann dann selbst auf das entsprechende Verzeichnis nicht mehr zugreifen, das Script also auch nicht ausführen, und reagiert dann mit der obigen Fehlermeldung, denn er findet ja keine Datei, die er ausführen könnte.&lt;/p&gt;

&lt;p&gt;Vielleicht hilfts ja jemanden, wenn ich ixs&amp;#8217; Analyse her noch einmal wiedergebe und verlinke. &lt;img src=&quot;https://netz-rettung-recht.de/plugins/serendipity_event_emoticate/img/emoticons/smile.png&quot; alt=&quot;:-)&quot; class=&quot;emoticon&quot; /&gt;&lt;/p&gt;
&lt;img src=&quot;https://ssl-vg03.met.vgwort.de/na/d2c9ab8906a6430ab9a4e3666d35e65c&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;&quot;&gt; 
    </content:encoded>

    <pubDate>Fri, 17 Apr 2009 15:03:00 +0000</pubDate>
    <guid isPermaLink="false">https://netz-rettung-recht.de/archives/1403-guid.html</guid>
    <category>Apache</category>
<category>Debian</category>
<category>Lenny</category>
<category>PHP</category>

</item>

</channel>
</rss>
