PHP - Fehlerbehandlung im Autoloader
Rolf b
- oop
- php
2 dedlfix0 Matthias Apsel0 dedlfix0 Matthias Apsel0 dedlfix
0 TS- dateisystem
- oop
- php
0 Rolf b2 mermshaus
Hallo,
in einem PHP Projekt habe ich einen Autoloader für Klassen gebaut. Eigentlich tut er seinen Job ganz gut und ihm ist PSR-4 auch nicht ganz fremd, aber zwei Dinge passen mir nicht.
Wenn ich die Klasse DingsBums suche und die Datei Dingsbums.php heißt, geht's schief, weil das Filesystem am Server case-sensitive ist. Entsprechendes gilt in den Namespaces, die ich auf Ordner mappe. Kann man mit PHP unter Linux irgendwie ein Windows-Verhalten bei der Dateisuche erreichen (sprich: case-insensitive)?
Wenn die Datei hugo.php nicht die Klasse hugo, sondern die Klasse otto enthält. Ist natürlich ein Programmiererfehler, aber ich würde in dem Fall doch ganz gerne eine ordentliche Fehlermeldung ausgeben und nicht mit der Nase voran in den Fatal Error Handler rennen.
Nun kann ich nach dem require $classFile
natürlich mit class_exists($classname)
fragen, ob die Klasse erfolgreich geladen wurde. Aber das ist nur die halbe Wahrheit, Interfaces laufen auch über den Autoloader und demzufolge muss ich class_exists($classname) || interface_exists($classname)
fragen. Ich habe es mal eingebaut und merke auch keinen Laufzeitnachteil (was aber auch dran liegen kann, dass ich nicht hunderte von Klassen lade sondern maximal 30 oder so). Wenn ich jetzt eine Klasse nicht finde, werfe ich eine handgemachte ClassNotFoundException und fange sie in meinem Rahmen-Exceptionhandler ein, um dann eine Fehlermeldung zu präsentieren.
Aber irgendwie finde ich das arg umständlich. Kann mir jemand einen Pointer auf die best-practice dazu geben? Oder mache ich genau das, was in PHP state-of-the-rubbish ist? Ich sitze btw noch auf PHP 5.6.
Rolf
Tach!
1. Wenn ich die Klasse DingsBums suche und die Datei Dingsbums.php heißt, geht's schief, weil das Filesystem am Server case-sensitive ist. Entsprechendes gilt in den Namespaces, die ich auf Ordner mappe. Kann man mit PHP unter Linux irgendwie ein Windows-Verhalten bei der Dateisuche erreichen (sprich: case-insensitive)?
Da würde ich mit PHP-Meldung abbrechen (lassen). Das ist ein Fehler, den der Entwickler sehr einfach durch richtige Schreibweise beseitigen kann. Da muss ich nicht für immer und ewig Korrektur-Code laufen lassen.
2. Wenn die Datei hugo.php nicht die Klasse hugo, sondern die Klasse otto enthält. Ist natürlich ein Programmiererfehler, aber ich würde in dem Fall doch ganz gerne eine ordentliche Fehlermeldung ausgeben und nicht mit der Nase voran in den Fatal Error Handler rennen.
Da würde ich mir auch keine Umstände machen. Das fällt doch eigentlich auch schon beim ersten Testlauf auf und ist nach der Korrektur ebenfalls endgültig beseitigt.
dedlfix.
Hallo Rolf b,
- Wenn ich die Klasse DingsBums suche und die Datei Dingsbums.php heißt, geht's schief, weil das Filesystem am Server case-sensitive ist. Entsprechendes gilt in den Namespaces, die ich auf Ordner mappe. Kann man mit PHP unter Linux irgendwie ein Windows-Verhalten bei der Dateisuche erreichen (sprich: case-insensitive)?
strtolower
verwenden?
Bis demnächst
Matthias
Tach!
strtolower
verwenden?
Hilft nur, wenn alle Dateien komplett kleinbuchstabig geschrieben wären.
dedlfix.
Hallo dedlfix,
strtolower
verwenden?Hilft nur, wenn alle Dateien komplett kleinbuchstabig geschrieben wären.
Kann man strtolower
nicht auf die Dateinamen anwenden?
Bis demnächst
Matthias
Tach!
strtolower
verwenden?Hilft nur, wenn alle Dateien komplett kleinbuchstabig geschrieben wären.
Kann man
strtolower
nicht auf die Dateinamen anwenden?
Ja, aber das nützt nichts, weil man der Dateifunktion die richtige Schreibweise mitgeben muss. Case-insensitives Ansprechen geht bei einem case-sensitiven Dateisystem nicht, weil Foo.txt und foo.txt zwei Dateien sein können und dann nicht eindeutig ist, welche nun gemeint sei.
dedlfix.
Hallo dedlfix,
Ja, aber das nützt nichts, weil man der Dateifunktion die richtige Schreibweise mitgeben muss. Case-insensitives Ansprechen geht bei einem case-sensitiven Dateisystem nicht, weil Foo.txt und foo.txt zwei Dateien sein können und dann nicht eindeutig ist, welche nun gemeint sei.
Ja, dass es nicht eindeutig sein muss, ist klar. Für diesen Fall müsste dann die Klasse eine Fehlermeldung bereit stellen.
Alle Dateinamen in ein Array laden, kleinschreiben und mit dem kleingeschriebenen Suchstring vergleichen müsste doch aber gehen?
Bis demnächst
Matthias
Hallo und gute Nacht,
Hallo dedlfix,
Alle Dateinamen in ein Array laden, kleinschreiben und mit dem kleingeschriebenen Suchstring vergleichen müsste doch aber gehen?
Rechne doch mal, wieviele Möglichkeiten der Groß-/Kleinschreibung ein 12stelliger Dateiname haben kann. Viel Spass dabei, sich die ganzen Handles darauf zu besorgen und dann herauszufinden, wrlche der Dateien denn die richtige ist.
Grüße
TS
Hallo TS,
Rechne doch mal, wieviele Möglichkeiten der Groß-/Kleinschreibung ein 12stelliger Dateiname haben kann. Viel Spass dabei, sich die ganzen Handles darauf zu besorgen und dann herauszufinden, wrlche der Dateien denn die richtige ist.
Das muss ich ja nicht. Wenn mehrere Dateien passen, gibts eine Fehlermeldung.
Bis demnächst
Matthias
Hallo und guten Morgen,
Rechne doch mal, wieviele Möglichkeiten der Groß-/Kleinschreibung ein 12stelliger Dateiname haben kann. Viel Spass dabei, sich die ganzen Handles darauf zu besorgen und dann herauszufinden, wrlche der Dateien denn die richtige ist.
Das muss ich ja nicht. Wenn mehrere Dateien passen, gibts eine Fehlermeldung.
Du verstehst noch nicht: In dem casesensitiven Linux-Filesystem musst Du die Datei erst einmal finden. Um ein Handle darauf zu bekommen, benötigst Du die genaue Schreibweise. Dann hast Du eine Seite des Vergleichs. Die andere Seite kannst Du dann durch mb_strtolower() schicken.
Grüße
TS
Hallo TS,
Du verstehst noch nicht: In dem casesensitiven Linux-Filesystem musst Du die Datei erst einmal finden. Um ein Handle darauf zu bekommen, benötigst Du die genaue Schreibweise. Dann hast Du eine Seite des Vergleichs. Die andere Seite kannst Du dann durch mb_strtolower() schicken.
Ob man das so machen sollte, steht auf einem anderen Blatt:
Bis demnächst
Matthias
Tach!
Ob man das so machen sollte, steht auf einem anderen Blatt:
Zumindest kann man so vorgehen, wenn man mit von Anwendern benannten Dateien umgehen muss. Wobei man dann eigentlich auch gleich beim Upload für eine eindeutige Benamsung Sorge tragen könnte.
Von Entwicklern hingegen kann man eher noch eine Fehlerkorrektur verlangen und sich dann das aufwendige Fehlerkorrigieren bei jeder Verwendung sparen.
dedlfix.
Hallo und guten Morgen,
Du verstehst noch nicht: In dem casesensitiven Linux-Filesystem musst Du die Datei erst einmal finden. Um ein Handle darauf zu bekommen, benötigst Du die genaue Schreibweise. Dann hast Du eine Seite des Vergleichs. Die andere Seite kannst Du dann durch mb_strtolower() schicken.
Ob man das so machen sollte, steht auf einem anderen Blatt:
- Ich hole mir alle Dateinamen im entsprechenden Ordner und schreibe die klein.
- Die vergleiche ich alle mit dem kleingeschriebenen Suchstring
- Gibt es nicht genau einen Treffer, muss die Klasse einen Fehler werfen.
Wie tief liegen denn die Klassendateien in der Verzeichnisstruktur?
Sind sie relativ adressiert? Wieviele Directories sind noch enthalten im Pfad?
Das betrifft schließlich auch die Schreibweise der Directory-Namen.
Grüße
TS
Tach!
Alle Dateinamen in ein Array laden, kleinschreiben und mit dem kleingeschriebenen Suchstring vergleichen müsste doch aber gehen?
Damit verlagerst du das Problem nur vom Dateisystem zu dir. Du kannst eine Liste der Dateinamen holen, diese in Kleinschreibung umwandeln und dann in der Liste nach dem kleingeschriebenen Namen suchen. Dann bekommst du eben dort zwei Einträge und weißt nicht, welcher der richtige ist.
dedlfix.
Hallo und gute Nacht,
in einem PHP Projekt habe ich einen Autoloader für Klassen gebaut. Eigentlich tut er seinen Job ganz gut und ihm ist PSR-4 auch nicht ganz fremd, aber zwei Dinge passen mir nicht.
- Wenn ich die Klasse DingsBums suche und die Datei Dingsbums.php heißt, geht's schief, weil das Filesystem am Server case-sensitive ist. Entsprechendes gilt in den Namespaces, die ich auf Ordner mappe. Kann man mit PHP unter Linux irgendwie ein Windows-Verhalten bei der Dateisuche erreichen (sprich: case-insensitive)?
Dafür gibt es Schmuddellösungen unter Linux. Es gibt auch einen caseinsensitiven ext3-driver. Leider ist der Link darauf, den ich hatte, broken.
Beide Lösungen funktionieren aber nur, wenn man sie aktiviert, bevor man Verzeichnis- und Dateinamen erzeugt.
Besser wird es aber sein, wenn Du Dedlfix's Vorschlag berücksichtigst und die Namen fixed.
Grüße
TS
Danke für eure Stellungnahmen.
Die Idee, das Directory einzulesen und dann case-insensitive durch das erhaltene Array zu turnen, hatte ich auch schon - aber das ist bei einem Pfad mühsam (weil ich das dann pro Ebene machen muss) und performant ist das garantiert mal gar nicht. Wenn es für Linux nicht idiomatisch ist - alles klar, dann muss der mit Windows-sozialisierte Rolf eben dazulernen :)
Gute Nacht zusammen
Rolf
Der Witz bei dieser Geschichte ist, dass Klassennamen (hier der Einfachheit halber verwendet als Oberbegriff für Klassen, Interfaces, … mit oder ohne Namespace) in PHP case-insensitive sind.
Die new
-Statements in diesem Code laufen völlig problemlos:
<?php
namespace foo {
class Bar {}
}
namespace {
var_dump(new foo\BAR(), new FOO\bar());
}
Das ist im Kontext von Autoloading durchaus ein Thema, über das diskutiert wird:
Aus Composer-Sicht besteht das Problem in diesem SELFHTML-Thread hier eigentlich nicht in den Verzeichnis- und Dateinamen, da mit der Classmap-Funktionalität problemlos (wie in „macht das Tool automatisch“) eine Zuordnung von Klassennamen zu Dateien geschaffen werden kann.
Composer generiert beispielsweise so was:
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Bar' => $baseDir . '/src/Qux.php',
'Foo' => $baseDir . '/src/foo.php',
'Rez' => $baseDir . '/src/Rez.php',
'RezInterface' => $baseDir . '/src/rezinterface.php',
);
Wenn du, Rolf, die Klassennamen im Code immer in der Groß-/Kleinschreibung nutzt, wie du sie in den Sourcecode-Dateien deklariert hast, kannst du Composer mit einer Classmap für deinen eigenen Code nutzen und ganz normal Composer als Autoloader einbinden (require __DIR__ . '/vendor/autoload.php'
) und bist sofort alle Probleme mit falschen Dateinamen los. Auch wenn es vielleicht nicht maximal elegant ist, eine Klasse otto
in einer Datei hugo.php
zu deklarieren und dergleichen.
Knifflig wird es nur, wenn du dann new Hugo()
schreibst statt new hugo()
, weil die Klassennamen (die Schlüssel) in der Classmap von Composer case-sensitive sind (siehe dazu die oben verlinkte Diskussion auf GitHub).
Das lässt sich lösen, indem man als Fallback noch einen Autoloader in grob dieser Form hinzufügt:
<?php
require __DIR__ . '/vendor/autoload.php';
spl_autoload_register(function ($className) {
static $classmapLower = null;
if (null === $classmapLower) {
$classmap = require __DIR__ . '/vendor/composer/autoload_classmap.php';
$classmapLower = array();
foreach ($classmap as $key => $value) {
$keyLower = strtolower($key);
if (isset($classmapLower[$keyLower])) {
throw new Exception('Duplicate class name: ' . $keyLower);
}
$classmapLower[$keyLower] = $value;
}
}
$classNameLower = strtolower($className);
if (isset($classmapLower[$classNameLower])) {
require $classmapLower[$classNameLower];
}
});
(Eventuell bietet es sich noch an, den Fallback auf bestimmte Namespaces/Verzeichnisse zu beschränken.)
Mit meiner Beispiel-Classmap oben würden mit…
var_dump(
new rez(),
new REZ(),
new bar(),
new Foo()
);
…dann die Klassen Rez
und Bar
über den Fallback-Loader geladen, während Foo
(und indirekt RezInterface
) über den normalen Composer-Loader geladen würden.
Abschließend zur Frage, ob es sinnvoll ist, so vorzugehen: Prinzipiell schließe ich mich @dedlfix an und empfehle, die Groß-/Kleinschreibung im Code und im Dateisystem zu beheben (und in jedem Fall „schlechte“ Windows-Gewohnheiten aufzugeben ;)). Ich erkenne aber an, dass es Szenarien geben kann, in denen das nicht praktikabel ist (erneut der Verweis auf den verlinkten GitHub-Thread). Für diese Fälle halte ich eine Lösung, wie ich sie hier vorgestellt habe (im Grunde auch eine – meines Erachtens relativ saubere – Umsetzung der Idee von @Matthias Apsel), für eine gangbare Alternative, bei der man sich aber darüber bewusst sein sollte, dass sie zusätzliche Komplexität und damit mögliche Fehlerquellen hinzufügt. Zudem funktioniert die vorgestellte Variante nur für Classmap-Autoloader und nicht für PSR-0- oder PSR-4-Loader. (Tipp am Rande: Bei einem Classmap-Loader immer composer update
oder einen vergleichbaren Befehl ausführen, sobald sich was an den Klassen-/Dateinamen ändert.)
PS: Der Classmap-Generator von Composer kann auch komplizierte Definitionen von Namespaces und Klassen innerhalb einer Datei korrekt verarbeiten. In meinen Augen ist das eine sehr solide Funktionalität, die in einigen eher kniffligen Fällen extrem hilfreich sein kann. Classmap-Loader gelten zudem als die performance-optimierte Variante von PSR-0- und PSR-4-Loadern. Siehe dazu auch:
--optimize-autoloader (-o): Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default.
Aha.
Composer habe ich mir bisher nicht angeschaut, es klingt aber nach einer Wissenschaft für sich. Mal gucken. Im Moment bin ich mit meinem handgemachten Simpel-Autoloader, der fix einen Namespaceraum auf eine Verzeichnisstruktur mappt , noch zufrieden. Bis auf die genannten Details.
Trotzdem danke für den ausfühlichen Tipp.
Rolf
Composer habe ich mir bisher nicht angeschaut, es klingt aber nach einer Wissenschaft für sich. Mal gucken. Im Moment bin ich mit meinem handgemachten Simpel-Autoloader, der fix einen Namespaceraum auf eine Verzeichnisstruktur mappt , noch zufrieden. Bis auf die genannten Details.
Composer kann auf den ersten Blick etwas komplex wirken, da bin ich durchaus bei dir. Es ist allerdings mittlerweile auch das Standardtool für Autoloading/Package-Management in PHP. Wie es bei derlei Tools häufig der Fall ist, nutzt man auch bei Composer dann im Normalfall die meiste Zeit nur drei oder vier Funktionen.
Das Setup unter Windows ist ein simpler Installer.[1] Wenn du das ausführst, solltest du danach in einem Terminal[2] composer --version
tippen können. Wenn das was Sinnvolles ausgibt, ist das Tool funktionsfähig.
Ich glaube, es ist nicht sinnvoll, wenn ich jetzt die Doku nacherzähle, aber ein kurzes „Hello World“-Beispiel, das die maßgebliche Funktionalität demonstriert, möchte ich dennoch liefern. Ich erwarte natürlich nicht, dass du das sofort ausprobierst oder dein Projekt umbaust. Ich schreibe den Text auch, um für zukünftige Nutzung so was zu haben. Letztlich kann ich jedem PHP-Entwickler die Nutzung von Composer nur sehr ans Herz legen. Nebenbei möchte ich auch immer empfehlen, eine etwaige Scheu vor der Nutzung der Kommandozeile (Terminal-/Konsolen-Anwendungen) abzulegen. Eine Nutzung vereinfacht Workflows oft erheblich und macht viele Dinge überhaupt erst möglich.
Vorsichtshalber der Hinweis: Diese Kurzeinführung stellt eigentlich keine Operationen vor, mit denen versehentlich die falschen Dateien gelöscht oder überschrieben werden könnten, aber vor allem bei wichtigen Sachen schadet ein vorheriges Backup natürlich nie.
Schnelleinstieg
Wenn du irgendwo ein Verzeichnis (myproject
oder so) für ein Demoprojekt anlegst, kannst du dort eine composer.json
-Datei mit dem folgenden Inhalt hinzufügen:
{
"require": {
"league/commonmark": "^0.15"
}
}
Das würde aussagen, dass per Composer das CommonMark-Package (ein Markdown-Parser) eingebunden und verwaltet werden soll.[3]
Wenn du nun im Terminal in das myproject
-Verzeichnis navigierst und dort composer install
tippst, lädt Composer automatisch das Package herunter[4] und installiert es ins vendor
-Unterverzeichnis.
$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing league/commonmark (0.15.0)
Loading from cache
league/commonmark suggests installing league/commonmark-extras (Library of useful extensions including smart punctuation)
Writing lock file
Generating autoload files
Im vendor
-Verzeichnis wird zudem ein Autoloader generiert, den du nun nur noch in deinem eigentlichen Code einzubinden brauchst, um die Inhalte (Klassen, Interfaces, …) aller mit Composer verwalteten Packages zu nutzen. Dieser Autoloader wird per require __DIR__ . '/vendor/autoload.php';
eingebunden.
Wenn du eine Datei test.php
im Projektverzeichnis (myproject
) erstellst und dort diesen Code einfügst…
<?php
require __DIR__ . '/vendor/autoload.php';
$converter = new League\CommonMark\CommonMarkConverter();
echo $converter->convertToHtml('# Hello World!');
…, hast du zum Beispiel ein fertiges Script, das Markdown-Quellcode in HTML umwandeln kann. Diese require
-Zeile und die composer.json
-Datei sind nahezu immer die einzige Anbindung, die Composer benötigt.
Wichtig ist an der Stelle: Composer wird in aller Regel nur lokal ausgeführt. Wenn du die test.php
nun beispielweise auf einem Webspace ausführen möchtest (sozusagen ein Deployment des Projekts), musst du lediglich die Datei und das vendor
-Verzeichnis hochladen. Composer selbst muss (und sollte) auf dem Zielsystem nicht installiert sein. Die fertige Software wäre also von den Abhängigkeiten und vom Aufbau her genauso, wie wenn du den Quellcode das CommonMark-PHP-Packages von Hand in ein Verzeichnis vendor
kopiert und dort zudem einen Autoloader dafür erstellt hättest.
Eigenen Code ebenfalls über den Composer-Autoloader einbinden
Du schreibst davon, dass du deinen eigenen simplen Autoloader hast. Das ist natürlich völlig in Ordnung. Autoloading ist grundsätzlich ein gelöstes Problem und nichts, das komplizierter gemacht werden muss, als es ist.
Mit einem Code wie…
spl_autoload_register(function ($className) {
$path = __DIR__ . '/src/' . str_replace('\\', '/', $className) . '.php';
if (file_exists($path)) {
require $path;
}
});
…ist die Sache oft erledigt. Dieser Loader wäre auch perfekt kompatibel mit Composer, könnte also problemlos hinter der require
-Zeile von Composer stehen.
Allerdings kann man, wenn man schon Composer nutzt, auch diesen Loader mit in Composer integrieren. Dazu wäre ein entsprechender autoload
-Zusatz in der composer.json
nötig, der in diesem Fall alle PHP-Klassen (und dergleichen) aus dem src
-Unterverzeichnis per Autoloading verfügbar machen würde:
{
"require": {
"league/commonmark": "^0.15"
},
"autoload": {
"classmap": "src/"
}
}
Ich habe hier der Einfachheit halber die Classmap-Variante gewählt, da die prinzipiell immer funktioniert und dort Namespaces keine Rolle spielen. Eine Organisation im Dateisystem nach PSR-0 und PSR-4 wird aber ebenfalls unterstützt. In deinem Fall wäre alternativ wohl PSR-4 angebracht. Dazu müsstest du bei Interesse kurz in die Doku schauen, weil bei dem Format der oberste Namespace mit angegeben werden muss, den ich natürlich nicht kenne.
Nach Änderungen in composer.json
muss im Projektverzeichnis einmal composer update
ausgeführt werden, um beispielsweise den Autoloader in vendor
entsprechend zu aktualisieren. Ist dies geschehen, kann in diesem Fall der eigene Autoloader aus dem Code entfernt werden. Der Code in src
kann nun auch mit vendor/autoload.php
geladen werden.
Warum Composer?
Der wesentliche Einsatzzweck in eigenen Projekten liegt natürlich im Einbinden und Verwalten von Third-Party-Packages. Schreibt man selbst an Code, der von anderen Entwicklern eingebunden werden können soll, gehört es heutzutage zudem fast schon zum „guten Ton“, diese Library auch als per Composer nutzbares Package anzulegen.
Die eben vorgestellte Autoloading-Funktionalität für eigenen Code ist aber immer ein netter Bonus. Siehe dazu auch meine vorherige Antwort.
Eine composer.json
-Datei eines privaten Projekts (Fußball-Tippspiel) von mir sieht beispielsweise so aus:
{
"config": {
"optimize-autoloader": true
},
"require": {
"roave/security-advisories": "dev-master",
"beberlei/assert": "~2.4",
"kaloa/renderer": "0.1.0",
"kaloa/view": "0.1.0",
"silex/silex": "~1.3"
},
"autoload": {
"psr-4": {
"Fuppes\\": "library/Fuppes"
}
}
}
Mein eigener Code liegt in library/Fuppes
und nutzt PSR-4 (na ja, mehr oder weniger). Dazu nutze ich vor allem als Framework Silex und einige andere Packages, die nicht so wichtig sind.[5] Das vendor
-Verzeichnis, in das Composer die Packages installiert, enthält derzeit etwas über 1000 Dateien. Das braucht mich als Entwickler aber nicht groß zu interessieren, da Composer das alles für mich verwaltet. Ich schreibe in meiner Anwendung nur am Anfang einmal require __DIR__ . '/vendor/autoload.php';
und kann dann alle Klassen nutzen.
Unter Linux habe ich es nicht so gemacht, wie es in der Doku vorgeschlagen wird. Ich habe einfach die composer.phar
runtergeladen und sie als composer
nach ~/bin
verschoben und dann vermutlich noch chmod +x ~/bin/composer
gemacht. Ich glaube, der Grund dafür war, dass ich die Doku zu dem Zeitpunkt einfach nicht gesehen hatte und dass ich es mit PHPUnit aus diversen Gründen (IDE-Integration) auch immer so ähnlich mache. Es gibt jedenfalls verschiedene Möglichkeiten, die alle irgendwie zum Ziel führen. ↩︎
Ein einfacher Weg, unter Windows eins zu öffnen, geht glaube ich so: Startmenü öffnen (Windows-Taste), cmd
tippen, Enter drücken. ↩︎
Das Package beberlei/assert ist eine Empfehlung für einigermaßen schmerzfreie typsichere Programmierung vor PHP 7. ↩︎
Hallo mermshaus,
ich habe mal deine Fußnoten formatiert. Ich hoffe, in deinem Sinne. Du brauchst die Fußnoten, wenn du sie mit [^Zahl]
und [^Zahl]:
erzeugst, übrigens nicht selbst ans Ende des Beitrags zu schreiben. Es macht das Schreiben manchmal einfacher, wenn Fußnote und Fußnotentext nah beieinander stehen.
Bis demnächst
Matthias
Super. Danke, sehr gut.[1]
Gibt es auch einen Weg, automatisch zu nummerieren, also nicht selbst die passende Abfolge der Zahlen sicherstellen zu müssen? Das stört nämlich, wenn man nachträglich eine Fußnote ergänzen möchte. Zum Beispiel: [^]
bezieht sich automatisch auf das nächste folgende [^]:
und fügt die passende Nummer automatisch hinzu.
Ja, es ist ganz erheblich besser, die Fußnoten direkt am Text haben zu können. ↩︎
Hallo mermshaus,
Gibt es auch einen Weg, automatisch zu nummerieren, also nicht selbst die passende Abfolge der Zahlen sicherstellen zu müssen? Das stört nämlich, wenn man nachträglich eine Fußnote ergänzen möchte. Zum Beispiel:
[^]
bezieht sich automatisch auf das nächste folgende[^]:
und fügt die passende Nummer automatisch hinzu.
Nein. Aber du kannst Fußnoten benennen statt sie zu numerieren:
Foo[^note] bar
[^note]: foobar
Der Name schlägt sich dann auch im Anker nieder.
LG,
CK
Okay, das ist ausreichend. Danke.[1]
Ich wünschte, CommonMark würde auch zumindest Fußnoten spezifizieren… Von $$\LaTeX$$-Formeln will ich ja gar nicht anfangen. ↩︎
Hallo mermshaus,
Ich wünschte, CommonMark würde auch zumindest Fußnoten spezifizieren… Von $$\LaTeX$$-Formeln will ich ja gar nicht anfangen.
CommonMark sagt, dass das als Extension haben wollen. Warum ist mir allerdings auch schleierhaft…
LG,
CK
Hallo Christian Kruse,
Nein. Aber du kannst Fußnoten benennen statt sie zu numerieren:
Foo[^note] bar [^note]: foobar
Das geht offensichtlich nicht. Grade probiert. Mit [^früher] und [^früher]: Vielleicht liegts aber auch am Umlaut.
Bis demnächst
Matthias
Hallo Matthias,
Nein. Aber du kannst Fußnoten benennen statt sie zu numerieren:
Foo[^note] bar [^note]: foobar
Das geht offensichtlich nicht. Grade probiert. Mit [^früher] und [^früher]: Vielleicht liegts aber auch am Umlaut.
Doch, das geht[1]!
LG,
CK
Nur nicht mit Umlauten: ein Identifier darf nur aus [\w-]
bestehen, laut Parser. ↩︎
Hallo mermshaus,
Gibt es auch einen Weg, automatisch zu nummerieren, also nicht selbst die passende Abfolge der Zahlen sicherstellen zu müssen?
Es gibt nicht die Möglichkeit, nicht automatisch zu nummerieren, abgesehen von den benannten Fußnoten.
Die Nummerierung der Fußnoten beginnt bei 1, unabhängig von den gewählten Zahlen, die eigentlichen Fußnoten können als eigene Zeile in beliebiger Reihenfolge an eine beliebige Stelle des Textes gesetzt werden.
Hinweis [^27]
[^27]: Diese Fußnote ist die 27
Hinweis [^4]
[^4]: Diese Fußnote ist die 4
Hinweis [1]
Hinweis [2]
Bis demnächst
Matthias
Hallo Matthias,
Es gibt nicht die Möglichkeit, nicht automatisch zu nummerieren, abgesehen von den benannten Fußnoten.
Das stimmt nur halb: der Anker-Name ist die Zahl, die man gewählt hat.
LG,
CK
Hallo Christian Kruse,
Das stimmt nur halb: der Anker-Name ist die Zahl, die man gewählt hat.
Ähm, nö? Ich habe 27 und 4 gewählt, die Anker heißen aber 1 und 2.
Bis demnächst
Matthias
Hallo Matthias,
Das stimmt nur halb: der Anker-Name ist die Zahl, die man gewählt hat.
Ähm, nö? Ich habe 27 und 4 gewählt, die Anker heißen aber 1 und 2.
Der Text der Anker ist 1 und 2, aber der Anker ist 27 und 4:
https://forum.selfhtml.org/self/2016/oct/21/php-fehlerbehandlung-im-autoloader/1677910#m1677910-fn:27
https://forum.selfhtml.org/self/2016/oct/21/php-fehlerbehandlung-im-autoloader/1677910#m1677910-fn:4
Beachte die 27 und die 4 am Ende.
Das liegt daran, dass Kramdown die Ziffern in den Ankern wie benannte Fußnoten behandelt; es macht keinen Unterschied zwischen Ziffern und Namen.
LG,
CK
Ich glaube, wenn man in kramdown wirklich mal ernsthaft was mit etlichen Fußnoten schreibt, sollte man einfach passende Begriffe als Bezeichner verwenden.[^beispiel]
[1] Ich hatte in meinem Text vorhin zwar nur fünf Fußnoten, aber selbst da hat es mich schon genervt, beim Hinzufügen einer Fußnote die nachfolgenden hochzuzählen. Eine zusätzliche generische Option für automatisch hochzählende Fußnoten wäre für meinen Geschmack zwar noch schöner (und wäre mit Begriffen als Bezeichnern wohl auch problemlos zu mischen), aber der verfügbare Weg ist schon in Ordnung[2], und es ist schön, überhaupt Fußnoten zu haben. Das ist bei so Markup-Formaten/-Dialekten/-Implementationen generell leider eine Ausnahme.