toscho.design

Interne Shortlinks in WordPress

Einschub 11.01.2010: Der Nachfolger ›WordPress: Shortlinks reloaded‹ verbessert den hier aufgeführten Code deutlich.

Vor ein paar Tagen ist die Debatte darüber aufgeflammt, ob man Verkürzer-Dienste wie TinyURL.com noch benutzen sollte. Ich will die Argumente hier nicht nachkauen; am Ende dieses Beitrages findet ihr eine Linkliste. Aber ich habe eine gute Nachricht für WordPress-Nutzer: Da ist ein URL-Verkürzer schon eingebaut. Wir müssen ihn nur noch benutzen.

Die jungfräuliche WordPress-Installation generiert für jeden Artikel und jede Seite eine URL, die so aussieht: Basisverzeichnis/?p=Beitragsnummer.
Wer gerne suchmaschinenfreundliche URLs haben will, korrigiert das schnell.

Aber die alte Form funktioniert noch; sie leitet nur auf die neue um mit dem suchmaschinenfreundlichen Statuscode 301 (Moved Permanently). Außerdem ist sie hübsch kurz – zumindest dann, wenn das Blog im Hauptverzeichnis liegt und der Domainname nicht übermäßig lang ist.

Twitter beispielsweise kürzt eine URL automatisch, sobald sie 30 Zeichen überschreitet. Bei einer vierstelligen Beitragsnummer + http:// + /?p= bleiben damit für die »reine« Blogadresse noch 15 Zeichen. Ein Grund mehr, auf eine effiziente URL-Struktur zu achten und den www-Präfix endgültig zu entsorgen.

Wir müssen nun diese »Urform« herausfinden und den passenden Link erzeugen.

/**
 * Gibt die kurze URL eines Beitrages oder einer Seite zurück.
 * @author Thomas Scholz <http://toscho.de>
 * @return string
 */
function shorturl()
{
    global $post;
    return rtrim(get_option('home'), '/') . '/?p=' . $post->ID;
}

/**
 * Erzeugt einen Kurzlink.
 * @author Thomas Scholz <http://toscho.de>
 * @param string $type 'a' oder 'link' zur Auswahl des Elementes
 * @param string $text Linktext
 * @param bool $echo Direkte Ausgabe oder reine String-Rückgabe.
 * @return string
 */
function shortlink($type = 'a', $text = 'Kurzlink', $echo = TRUE)
{
    /* Nichts zu tun. */
    if ( !is_page() && !is_single() )
    {
        return;
    }
    /* Textlink. */
    if ( 'a' == strtolower( trim($type) ) )
    {
        $link = '<a class="shortlink" href="' . shorturl()
            . '" rel="alternate shorter">' . $text . '</a>';
    }
    /* Link-Element. */
    else
    {
        $link = '<link rel="alternate shorter" href="'
            . shorturl() . '" title="' . $text . '">' . "\n";
    }
    if ($echo)
    {
        echo $link;
        return;
    }

    return $link;
}

Diese beiden Funktionen kopieren wir in die functions.php des aktuellen Themes. Dann können wir beispielsweise in die single.php unter die Beitragsausgabe schreiben …

<?php shortlink(); ?>

… und schon haben wir einen Kurzlink.
Der Einbau des Link-Elementes in den HTML-Kopf ist ebenfalls einfach: In die Datei header.php schreiben wir …

<?php shortlink('link'); ?>

… und dann hoffen wir, daß irgendwann mal Twitter, Verkürzerdienste und Browser eine automatische Erkennung implementieren.

Lektüre

Alles Englisch.

Reicht euch das, oder soll ich Plugin daraus machen?

26 Kommentare

  1. Jared am 11.04.2009 · 15:09

    Hallo Thomas,
    super Sache und werde ich bestimmt irgendwann nochmal gebrauchen.
    Aber ist es Absicht das du beim <php das Fragezeichen vergessen hast?

  2. Thomas Scholz am 11.04.2009 · 16:54

    Nein, das war nackte Schlamperei. Ist repariert, danke.

  3. Steffen am 02.05.2009 · 19:02

    Wie ist das eigentlich. Wenn ich meine Permalinkstruktur auf /%post_id%/%postname% setze, dann sind die Artikel ja auch erreichbar, wenn ich nur die Post-ID eingebe, also http://domain.tld/Post-ID aufrufe.

    Allerdings gibt es dann noch keinen Redirect auf die URL nach meiner Permalinkstruktur und DC hin oder her, es ist sicher nicht gut wenn eine Seite unter zwei Adresse abrufbar ist. Der Redirect passiert bei mir erst, wenn ich mind. die ersten drei Zeichen von %postname% eingebe. Gibt es evt. eine Funktion, die das noch ergänzt, sodass ich mir nach deinem Beispiel hier oben noch das ?p= sparen kann?

  4. Thomas Scholz am 02.05.2009 · 19:25

    @Steffen: Eigentlich solltest du den Zugriff auf die reine Post-Id immer umleiten. Um Genaueres zu sagen, müßte ich wissen, wie deine Jahresarchive erreichbar sind. Wenn die nämlich beispielsweise unter example.com/2009/ liegen, wird es sehr schwierig, sie von »nackten« Post-Ids zu unterscheiden.

    Du könntest in die functions.php deines Themes so etwas schreiben:

    if ( is_singular() 
    	and !is_home() 
    	and !strpos($_SERVER['REQUEST_URI'], get_permalink() )
    	)
    {
    	header('Location: ' . get_permalink(), TRUE, 301);
    	exit;
    }

    Das habe ich jetzt nicht getestet; nimm es bitte nur als Gedankenanstoß.

    Im Grunde halte ich es für einen Bug in WordPress, daß die Umleitung nicht automatisch anspringt oder zumindest ein 404 erscheint. Vielleicht siehst du mal im Trac nach, ob das schon gemeldet wurde – und wenn nicht, dann schreib doch bitte einen Bugreport.

  5. Steffen am 02.05.2009 · 19:50

    @Thomas Scholz: die Jahresarchive sind unter example.com/date/2009 erreichbar. Ich werde den Code mal ausprobieren. Vielen Dank schonmal. Mal schauen ob ich das mit dem Bugreport hinbekomme, ich bin da sehr unbeleckt.

  6. Gregor am 13.06.2009 · 19:44

    Danke! Hab die Funktion leicht verändert eingebaut. Hab noch eine kürzere Domain (nur Nachname) und das dann statt rtrim(get_option('home'), '/') benutzt.

  7. Gregor am 13.06.2009 · 21:37

    Mir ist gerade aufgefallen: ich komme bei mir zwar mit der kurzen Adresse auf die Seite, aber ich werden nicht weitergeileitet zum "richtigen" URL. Woran könnte das liegen?

    Bei dir wird hier übrigens nur "/?p=762" als Kurz-URL angezeigt, muss da nicht noch die Domain vor?

  8. Thomas Scholz am 13.06.2009 · 21:54

    @Gregor: Warum du nicht weitergeleitet wirst, weiß ich nicht. Du könntest mit dem Code nachhelfen, den ich am 2. 5. Steffen empfohlen habe.

    Bei mir werden nur absolute Pfade ausgegeben, weil ich ganz zuletzt noch eine Funktion über die komplette Ausgabe laufen lasse, die den Protokollbezeichner (hier: http) und die Domain entfernt. Das spart ein bißchen Traffic und funktioniert genauso gut.

  9. Gregor am 13.06.2009 · 21:59

    Alles klar. Und wie kriegst du es hin, dass die Weiterleitung funktioniert und dabei auch noch der richtige Statuscode gesendet wird?

  10. Thomas Scholz am 13.06.2009 · 22:07

    Das macht WordPress eigentlich automatisch, wenn man Permalinks benutzt. Vielleicht werden deine Probleme dadurch verursacht, daß du das Schema /%postname% benutzt. Damit hat WordPress enorme Mühe; deshalb vermeide ich das.
    Ich kann mir gut vorstellen, daß damit neben den Performance-Einbrüchen auch ein Bug beim Herausfinden der kanonischen Adresse getriggert wird.

  11. Gregor am 13.06.2009 · 22:50

    Sorry, das hatte ich irgendwie überlesen. Aber hat auch nicht funktioniert. Ich habe dann dieses Plugin gefunden, damit klappt die Weiterleitung inkl. 301.

  12. Bernhard Häussner am 20.06.2009 · 14:01

    Eigentlich gibt es schon ein Shortlink Plugin für WordPress, das auch der rel="shortlink"-Specification genügt. ☺

  13. Thomas Scholz am 20.06.2009 · 14:09

    @Bernhard Häussner: »Die« Shotlink-Spezifikation gibt es (noch) nicht, nur mehrere konkurrierende private Initiativen. Ein einheitlicher Standard wäre aber sinnvoll.

    Zu deinem »schon«: Beachte das Datum dieses Beitrages. Als ich ihn geschrieben habe, existierte das Plugin noch nicht. ☺

    Ich bevorzuge auch die Lösung ohne Plugin: Das schont die Optionstabelle und erleichtert individuelle Anpassungen.

  14. Bernhard Häussner am 21.06.2009 · 10:21

    @Thomas Scholz: Hauptsächlich da es keinen einheitlichen Standard gibt, wollte ich auf die/eine Shortlink-Spezifikation aufmerksam machen, welche aus den semantischen und praktischen Problemen mit anderen "privaten Initiativen" entstanden ist.
    In der Tat habe ich angenommen, dass dein Beitrag neuer wäre, weil ich über einige Ecken von einem sehr aktuellen Beitrag zum Thema darauf gestoßen bin, der unter anderem vermutet, das erste deutschsprachige Medium mit Kurzurls zu sein, allerdings auch wieder auf eine andere Implementierung bezogen. Die Shortlink-Idee selbst war wahrscheinlich noch nicht sehr alt, als du deinen Beitrag verfasst hast.
    Wie auch immer, ich hatte gehofft, du würdest in Erwägung ziehen auch rel="shortlink" zu vewenden.
    Es könnte dich vielleicht auch interessieren, wie die Spezifikation die Kurzlinks im HTTP-Header versendet, sodass Cleints wie twitter, beim Eingeben einer langen URL in das Textfeld, nur ein sparsames HEAD-Request senden müssten.

  15. Thomas Scholz am 21.06.2009 · 13:19

    @Bernhard Häussner: Nachdem ich diese alternativen Vorschläge gelesen habe, bin ich noch nicht zu einem Ergebnis gekommen.

    rev="canonical" ist in nahezu jeder Hinsicht Unsinn.

    rel="shorturl", wie hier vorgeschlagen, beschränkt das Konzept unnötig auf URLs. Man müßte für die genereller angelegten URIs also wieder eine neue Konvention finden.

    rel="shortlink" bezeichnet die Adresse als Link. Stimmt auch nicht. Immerhin steht diese Version derzeit im HTML-5-Wiki.

    Und mein derzeit verwendetes rel="alternate shorter" sieht aus, als zeige es auf eine Version, die nur die Zusammmenfassung enthält.

    Nun könnte ich schreiben: rel="alternate shorter shorturl shortlink". Das sieht so dämlich aus, daß es funktionieren könnte. Aber ich sträube mich dagegen.

    Das muß ich noch in mir arbeiten lassen.

    Gibt es schon einen Twitterclient oder eine andere Software, die entsprechende HEAD-Requests aussendet, um die kurze Adresse zu ermitteln?

    Das von dir empfohlene Plugin prüft gar nicht erst die Request-Methode, sondern sendet den Header immer. Bei dem Gedanken, eine Million Header zu verschicken, damit vielleicht einer die Information verwertet, wird mir einfach schlecht.
    Das kann man sehr leicht besser machen.

    Unabhängig davon stehen wir vor einem Henne-Ei-Problem: Solange kaum eine Website diesen Header verschickt, wird kein Client seine Nutzer mit der Anfrage ausbremsen. Und solange kein Client danach sucht, lohnt sich der Header nicht. Hier müßten zwei große Spieler mit Pionierlaune parallel loslegen; Twitter und WordPress beispielsweise.
    Bis dahin gießen wir nur Wein ins Meer.

  16. Bernhard Häussner am 21.06.2009 · 22:30

    if (stripos($_SERVER['REQUEST_METHOD'], 'HEAD') !== FALSE 
         && !headers_sent() ) { 
      header('Link: ; rel=shortlink');
      exit(); 
    }
    

    Diese kleine Codeänderung in der shortlink_create()-Funktion des Plugins könnte das Problem der meist überflüssigen Header lösen.

    Und in der Tat haben alle Kurzlink-Alternativen das Problem, dass es anscheinend noch nicht wirklich viel Software gibt, die sie auch benutzt. Ich kenne zumindest keine, die sich nicht auf das reine finden von Kurzlinks beschränken. Diesen Missstand habe ich für mich gelöst, indem ich ein Bookmarklet gebastelt habe, das auf einer Webseite nach einem Kurzlink sucht, die Twitter /home-Seite öffnet und den Kurzlink einträgt. (Da Javascript verwendet wird, um auf der schon geladenen Seite zu suchen, macht ein HEAD-Request auch hier übrigens keinen Sinn)

    Die Häufung von Schlüsselwörtern halte ich auch nicht für sinnvoll, da jedes Schlüsselwort eigentlich eine abgeschlossene Einheit darstellen sollte. Das ältere und daher etablierte "shortcut icon" macht hier eigentlich die einzige - wohl falsche - Ausnahme.

    Du wirfst allerdings eine wirklich interessante Nuance des Problems auf: Die kurze Adresse ist eigentlich kein "Link", auch wenn sich in der Umgangssprache "Link" und "Webadresse" überschneiden.
    Wenn man diesen Faden weiter spinnt, stellt man fest, dass der Link nicht eine kurze URL verlinkt: Im Grunde ist die Ressource, die sich hinter der kurzen Adresse verbirgt die lange URL. So wie also rel="author" auf die Reccource "Autor" verweist, müsste konsequent das Schlüsselwort des Links auf ein Dokument, das nur eine Weiterleitung zur langen URL enthält, eigentlich ähnlich wie "forward" oder gar "url" lauten.
    Seltsam - erst beim Schreiben dieses Kommentars ist mir aufgefallen das aus diesem Gund eigentlich alle bisher vorgeschlagenen Keywords total falsch sind.

    Im Übrigen ist dann eine explizite Nennung des Prädikats "kurz" heraus gefallen. Eigentlich könnte der Client entscheiden, ob die angegebene Weiterleitungsadresse kürzer ist. Doch die reine Weiterleitung ist eigentlich zu wenig: Es bleibt die Frage, wie sich die Information, dass die URL für die Weiterleitung auch wirklich "die Kurze" ist, richtig überbringen lässt.

  17. Thomas Scholz am 21.06.2009 · 23:04

    @Bernhard Häussner: Die Überprüfung sollte besser so aussehen:

    if ( 'HEAD' == $_SERVER['REQUEST_METHOD'] ) {}

    Das geht schneller.

    Die Häufung von Schlüsselwörtern halte ich auch nicht für sinnvoll, da jedes Schlüsselwort eigentlich eine abgeschlossene Einheit darstellen sollte. Das ältere und daher etablierte "shortcut icon" macht hier eigentlich die einzige - wohl falsche - Ausnahme.

    Nein, das stimmt so nicht. Das Attribut rel darf durchaus mehrere durch Leerzeichen getrennte Werte haben, ähnlich wie class.
    Das Problem bei der von mir skizzierten langen Variante ist nicht syntaktischer Natur. Es ist einfach keine wirkliche Lösung, sondern illustriert nur die Hilflosigkeit des Autors.

    Seltsam - erst beim Schreiben dieses Kommentars ist mir aufgefallen das aus diesem Gund eigentlich alle bisher vorgeschlagenen Keywords total falsch sind.

    Na, da treffen wir uns doch. ☺
    shortaddress könnte ein passender Wert sein. Sieht aber auch … unbeholfen aus.

  18. Bernhard Häussner am 21.06.2009 · 23:36

    Ja, natürlich sind mehrere Werte möglich, wie bei class. Nur bei class bedeutet ja auch jeder Wert etwas eigenes, und so sollte auch bei rel die Kombination nicht die Aussage transportieren, sondern die Parallelität der Einzelaussagen.

    Offensichtlich haben wir uns noch nicht ganz getroffen. ☺ Ich wollte eigentlich darauf hinaus, dass alles, was sich auf die URL im link Tag bezieht eigentlich nicht ins rel Attribut passt, da das rel Attribut ja eine Information zum Dokument "hinter" diesem Link auszeichnet. Das Dokument hinter dem Link ist jedoch eine Weiterleitung, die nur eben unter einer kurzen URL zu finden ist.

    [The rel] attribute describes the relationship from the current document to the anchor specified by the href attribute. The value of this attribute is a space-separated list of link types.

    Ein Vorschlag/Beispiel:

    <link rel="forwarding" class="shortaddress" href="...">

    The class attribute has several roles in HTML [...] For general purpose processing by user agents

    Ich hoffe, das wäre ein Vorschlag der zwar nicht der ersten Intuition entspricht, aber umso mehr den standardisierten Bedeutungen der Attribute.

  19. Thomas Scholz am 21.06.2009 · 23:59

    @Bernhard Häussner: Okay, jetzt sehe ich es. Man kann mit link eigentlich nur etwas über die verlinkte Ressource aussagen, nicht aber über deren Adresse. Dein forwarding (der Present Progressive ist hier unüblich) bezieht sich dann auch – fälschlicherweise – auf die Ressource.
    rel="self" ist somit der einzig korrekte und darob schwachsinnige Wert, um die Beziehung treffend zu charakterisieren.

    Ist link vielleicht das falsche Element?

    Vorschlag:

    <meta http-equiv="X-Short-URI" content="http://toscho.de/?p=762">

    Das läßt sich sehr einfach in einen HTTP-Header übersetzen und trifft keine Falschaussagen über den Inhalt hinter der URI. Ein bißchen leichter zu parsen ist es auch.

  20. Bernhard Häussner am 22.06.2009 · 00:10

    Ich frage mich jetzt, wer eigentlich am Anfang die Idee hatte mit dem Link-Tag. Das ist definitiv ein guter Einfall. Nur:

    Das http-equiv-Attribut des meta-Elements ist in XHTML verboten und wird, falls vorhanden, ignoriert.

    Aus: HTML 5 und XHTML 5 im Vergleich (WHATWG) – Übersetzung, Jens Meiert, 27. Februar 2007.

    Das könnte ein Problem sein/werden.

  21. Thomas Scholz am 22.06.2009 · 00:25

    @Bernhard Häussner: Die von dir zitierte Aussage bezieht sich nur auf die Angabe der Zeichenkodierung im nachgeahmten Content-Type, nicht auf das Attribut selbst. Das ist in HTML 5 noch erlaubt.

    Das Problem bei der Standardisierung liegt woanders: Diese Erweiterung müßte in HTTP eingeführt werden, nicht in HTML 5. Und HTTP ist ungefähr so beweglich wie der Mount Everest.

  22. Bernhard Häussner am 22.06.2009 · 22:31

    Pragma directions corresponding to headers describing metadata, or not requiring specific user agent processing, must not be registered; instead, use metadata names.
    [...]
    Metadata names whose values are to be URLs must not be proposed or accepted. Links must be represented using the link element, not the meta element.

    Was bedeutet: Wenn URL als Matadaten, dann nur im Link-Element. Jetzt kann man sich darüber streiten, ob das in diesem Fall Sinn macht.
    Zudem wäre mit dem Verwenden des Link-Headers eine Erweiterung von HTTP Überflüssig.

  23. Thomas Scholz am 22.06.2009 · 22:53

    @Bernhard Häussner: Wir sind uns ja einig, daß eine Kurz-URL kein Link ist, sondern eine alternative Adresse. Um die zu benutzen ist eben doch ein spezifisches Verhalten des User-Agents notwendig – deshalb gibt es ja noch keine Implementation.

    Das »Verbot« in HTML 5 bezieht sich auch nur auf die Aufnahme ins Wiki. Für die Akzeptanz meines Vorschlages wäre das zwar hilfreich, aber nicht notwendig.

    Und im Grunde funktioniert das Ganze auch ohne Aufnahme in den HTTP-Standard, wenn es nur von genügend Software implementiert wird. Microsofts X-UA-Compatible hat das (leider) deutlich demonstriert.

  24. Bernhard Häussner am 22.06.2009 · 23:41

    @Thomas Scholz: Zumindest ist Microsoft schon ein bisschen zurück gerudert.

    Ja dann steht der neuen Variante eigentlich nichts im Weg: Jetzt muss es nur noch jemand implementieren...

  25. Sam Johnston am 18.08.2009 · 19:18

    If there was any doubt about which standard to use then the recent WordPress.com announcement should resolve it. They have just added rel=shortlink support in both the HTTP headers and HTML HEAD to over 100,000,000 pages spanning 7,000,000+ blogs.

    While you can use a space separated list to cover your bases it's better to choose one and encourage others to support it too. That's the only way we're ever going to resolve the growing 3rd party URL shortener problem and encourage clients to introduce support for it. Sure the semantics are not perfect but "shortlink" is a hell of a lot better than the various /short[_- ][ui]r[li]/ permutations.

    Sam

  26. Thomas Scholz am 18.08.2009 · 19:45

    @Sam Johnston: Yes, I’ve read this. I’ll go with this solution, but I still think it is the wrong way. Maybe I’ll use both, the real http-equiv notation and the link element.