1. Code
  2. WordPress
  3. Plugin Development

Eine Anleitung zur WordPress HTTP API: Automatische Plugin-Updates

Scroll to top

German (Deutsch) translation by Alex Grigorovich (you can also view the original English article)

Wie Sie schon wissen, verfügt WordPress über einen Mechanismus, der Plugins, Themes und die WordPress-Kernupdates erkennt, benachrichtigt, wenn sie verfügbar sind, Informationen zu diesen Updates abruft und es Ihnen ermöglicht, diese Updates automatisch zu installieren. In diesem dritten (und letzten) Teil der WordPress HTTP API-Reihe erfahren Sie, wie Sie Ihr eigenes Plugin-Repository erstellen, um automatische Updates für Ihre Benutzer zu verteilen.


Ein Blick auf Auto-Updates

WordPress verfügt über ein tolles Auto-Update-System, das Sie benachrichtigt, wenn neue Versionen des WordPress-Kerns, installierte Plugins oder Themes verfügbar sind. Die Benachrichtigungen werden in der Admin-Leiste und auf der Plugins-Seite angezeigt, auf der Sie weitere Informationen zur neuen Version erhalten.

Um die neue Version zu installieren, klicken Sie einfach auf "Automatisch aktualisieren". WordPress lädt das neue Paket automatisch herunter, extrahiert es und ersetzt die alten Dateien. Es ist kein FTP, Entfernung alter Dateien und Upload ist erforderlich.

Es gibt auch eine spezielle Seite für Updates, die über das Dashboard-Menü erreicht werden kann. Dies ist hilfreich, wenn Sie Massenaktualisierungen mehrerer Plugins durchführen möchten, anstatt jedes einzeln zu aktualisieren. Es hat auch eine "Check Again" -Button, die nach neuen Updates sucht. Standardmäßig führt WordPress diese Überprüfung alle 12 Stunden durch.

Unter der Haube

Automatische Updates sind wie Magie. Sie sind nur einen Klick von der neuesten Version entfernt. Die gute Nachricht ist, dass der Prozess selbst nicht kompliziert ist oder zumindest nicht verstanden und nicht für den eigenen Gebrauch angepasst werden kann. Es kann viele Gründe geben, warum Sie Ihren eigenen Server ausführen möchten, um Updates für Ihre Plugins zu verteilen. Das einzige, an das ich denke, sind Premium-Plugins. Wenn Ihr Plugin kostenlos und für die Öffentlichkeit zugänglich ist, sollten Sie es im WordPress-Plugins-Repository veröffentlichen.

Alle 12 Stunden sucht Ihr WordPress-Blog nach neuen Plugin-Versionen und speichert die gesendete Anfrage und die empfangene Antwort in einer vorübergehenden Site namens "update_plugins". Der folgende Code zeigt den Inhalt dieses Übergangs an:

1
add_filter ('pre_set_site_transient_update_plugins', 'display_transient_update_plugins');
2
function display_transient_update_plugins ($transient)
3
{
4
	var_dump($transient);
5
}

Aktualisieren Sie nach etwa 12 Stunden Ihre Blog-Plugins-Seite, und Sie sollten eine ähnliche Ausgabe wie die folgende erhalten:

1
object(stdClass)[18]
2
  public 'last_checked' => int 1333808132
3
  public 'checked' => 
4
    array
5
      'access/access.php' => string '1.0' (length=3)
6
      'adpress/wp-adpress.php' => string '3.1' (length=3)
7
          ...
8
      'wp-paypal/plug.php' => string '1.0' (length=3)
9
  public 'response' => 
10
    array
11
      'akismet/akismet.php' => 
12
        object(stdClass)[13]
13
          public 'id' => string '15' (length=2)
14
          public 'slug' => string 'akismet' (length=7)
15
          public 'new_version' => string '2.5.5' (length=5)
16
          public 'url' => string 'https://wordpress.org/extend/plugins/akismet/' (length=44)
17
          public 'package' => string 'http://downloads.wordpress.org/plugin/akismet.2.5.5.zip' (length=55)
18
      'update.tmp/plugin.php' => 
19
        object(stdClass)[12]
20
          public 'slug' => string 'plugin' (length=6)
21
          public 'new_version' => string '1.1' (length=3)
22
          public 'url' => string 'http://localhost/update.php' (length=27)
23
          public 'package' => string 'http://localhost/update.php' (length=27)

Die Ausgabe wird sicherlich von Blog zu Blog unterschiedlich sein, aber die enthaltenen Informationen sind im Wesentlichen dieselben:

  • last_checked (int) - Das letzte Mal, dass die Überprüfung der automatischen Aktualisierungen ausgeführt wurde (in Sekunden)
  • checked (array) - Die Liste der überprüften Plugins und ihrer Version
  • response (array) - Die vom API-Server zurückgegebene Antwort

Deaktivieren der 12-Stunden-Latenz

Wenn Sie wie ich sind, werden Sie wahrscheinlich nicht gerne 12 Stunden darauf warten, dass WordPress nach neuen Updates sucht, wenn Sie entsprechende Debugging- und Entwicklungsarbeiten durchführen. Dazu können Sie einfach auf der Update-Seite auf die Schaltfläche "Erneut prüfen" klicken. Die Wartezeit von 12 Stunden ist im WordPress-Kern in Zeile 147 der Datei wp-includes/update.php definiert. Dies ist die Variable $timeout.

Anschließen an den Transienten "update_plugins"

Da Ihr Plugin nicht im WordPress-Plugin-Repository gehostet wird, erfolgt keine Antwort von der herkömmlichen API. Die Lösung besteht darin, eine eigene API zu erstellen und nach Updates zu suchen. und wenn ein neues Update verfügbar ist, fügen Sie die Antwort zum Transienten "update_plugins" hinzu. WordPress lädt die neue Version von einer 'download_url' herunter, die Sie in der Antwort angegeben haben.

Um nach neuen Versionen zu suchen, benötigen wir einen API-Server. Es ist nicht unbedingt erforderlich, dass Ihr Server über PHP oder sogar eine serverseitige Sprache verfügt, aber dies hilft bei der genaueren Kontrolle des Aktualisierungsprozesses. In diesem Tutorial wird davon ausgegangen, dass Sie einen Remote-PHP-Server haben, aber Sie können den Code (der ziemlich einfach ist) in Ihre Lieblingssprache konvertieren.

Um ein besseres Verständnis der Funktionsweise des Filters zu erhalten, wählen Sie ein Plugin aus der Liste "Aktiviert" aus, das in Ihrem Blog angezeigt wird. Zum Beispiel das Plugin 'access/access.php' und fügen Sie den folgenden Code in ein neues oder bereits laufendes WordPress-Plugin ein.

1
add_filter ('pre_set_site_transient_update_plugins', 'display_transient_update_plugins');
2
function display_transient_update_plugins ($transient)
3
{
4
	$obj = new stdClass();
5
	$obj->slug = 'access.php';
6
	$obj->new_version = '2.0';
7
	$obj->url = 'http://anyurl.com';
8
	$obj->package = 'http://anyurl.com';
9
	$transient[plugin_directory/plugin_file.php] => $obj;
10
	return $transient;
11
}

Wenn Sie jetzt Ihre Plugins-Seite aktualisieren, sollte eine Benachrichtigung angezeigt werden, dass eine neue Version des Plugins verfügbar ist (Version 2.0), und Sie können weitere Informationen erhalten oder diese installieren. Natürlich wird noch nichts davon funktionieren, aber Sie haben die Idee. WordPress verlässt sich auf diesen Übergang, um über die neuen Updates informiert zu werden.

Zusammenfassend muss unsere Lösung Folgendes tun:

  1. Suchen Sie nach neuen Updates von Ihrem Server.
  2. Wenn ein neues Update vorhanden ist, fügen Sie es der Antworteigenschaft des Transienten 'update_plugins' hinzu.
  3. Haken Sie auf ähnliche Weise mit "plugins_api" ein, um die neuen Versionsinformationen bereitzustellen.
  4. Ein API-Server, der die Version, Details und das Paket der neuesten Version bereitstellt.

Die Auto-Update-Klasse

Um Dinge zu standardisieren und zu rationalisieren, habe ich eine PHP-Klasse erstellt, die mit jedem Plugin funktioniert. Die Verwendung ist recht einfach. Sie müssen lediglich drei Parameter angeben: die aktuelle Version des Plugins, den Aktualisierungspfad und den Slug des Plugins. Ich werde später im Detail erklären, was jeder tut.

Unsere Klasse kann 3 HTTP-Anforderungen ausführen, 2 erforderliche und eine dritte optionale:

  1. Bei der ersten Anforderung wird auf dem Remote-Server nach der neuesten Version gesucht. Um die Dinge einfacher und schneller zu machen, gibt der API-Server nur die neueste Version als Zeichenfolge zurück (z. B. "1.0" oder "1.1").
  2. Die zweite Anforderung gibt die neuesten Versionsinformationen als serialisiertes PHP-Objekt zurück.

Die Klasse verfügt über 3 öffentliche Funktionen, die Sie in Ihrem Plugin verwenden können:

  • getRemote_version - Gibt die neueste Remote-Version als Zeichenfolge zurück.
  • getRemote_description - Gibt die neuesten Remote-Versionsdetails als PHP-Objekt zurück.
  • getRemote_license - Gibt den Lizenzstatus des Plugins zurück. Es ist optional und in diesem Lernprogramm wird keine Lizenz implementiert.

Verwenden der Klasse

Die Klasse kann in der Aktion "init" initiiert werden. Es hat 3 Parameter:

  1. Aktuelle Version - Eine Zeichenfolge mit der aktuell installierten Version des Plugins.
  2. Update-Pfad - Der Pfad des Update-Servers. In unserem Fall handelt es sich um eine PHP-Datei in meinem localhost-Stammverzeichnis.
  3. Plugin-Slug - Dies ist erforderlich, um den Plugin-Slug und den Namen zu erhalten. Zu diesem Zweck sollte die Klasse in derselben Datei initiiert werden, die den WordPress-Plugin-Header enthält.
1
add_action('init', 'wptuts_activate_au');
2
function wptuts_activate_au()
3
{
4
	require_once ('wp_autoupdate.php');
5
	$wptuts_plugin_current_version = '1.0';
6
	$wptuts_plugin_remote_path = 'http://localhost/update.php';
7
	$wptuts_plugin_slug = plugin_basename(__FILE__);
8
	new wp_auto_update ($wptuts_plugin_current_version, $wptuts_plugin_remote_path, $wptuts_plugin_slug);
9
}

Das ist alles! Jetzt ist Ihr Plugin bereit, Updates vom angegebenen Remote-Pfad abzurufen.


Der Klassencode

Schritt 1 Die Klasse, Eigenschaften und Methoden

Wir beginnen mit einem Klassenskelett, das die Eigenschaften, den Konstruktor und die verschiedenen Methoden definiert. Vor jeder Eigenschaft und Methode befindet sich ein Kommentarbereich mit einer Beschreibung, den akzeptierten Parametern und der zurückgegebenen Variablen. Die Kommentare folgen den PhpDoc-Richtlinien.

1
class wp_auto_update
2
{
3
    /**

4
     * The plugin current version

5
     * @var string

6
     */
7
    public $current_version;
8
9
    /**

10
     * The plugin remote update path

11
     * @var string

12
     */
13
    public $update_path;
14
15
    /**

16
     * Plugin Slug (plugin_directory/plugin_file.php)

17
     * @var string

18
     */
19
    public $plugin_slug;
20
21
    /**

22
     * Plugin name (plugin_file)

23
     * @var string

24
     */
25
    public $slug;
26
27
    /**

28
     * Initialize a new instance of the WordPress Auto-Update class

29
     * @param string $current_version

30
     * @param string $update_path

31
     * @param string $plugin_slug

32
     */
33
    function __construct($current_version, $update_path, $plugin_slug)
34
    {
35
    }
36
37
    /**

38
     * Add our self-hosted autoupdate plugin to the filter transient

39
     *

40
     * @param $transient

41
     * @return object $ transient

42
     */
43
    public function check_update($transient)
44
    {
45
    }
46
47
    /**

48
     * Add our self-hosted description to the filter

49
     *

50
     * @param boolean $false

51
     * @param array $action

52
     * @param object $arg

53
     * @return bool|object

54
     */
55
    public function check_info($false, $action, $arg)
56
    {
57
    }
58
59
    /**

60
     * Return the remote version

61
     * @return string $remote_version

62
     */
63
    public function getRemote_version()
64
    {
65
    }
66
67
    /**

68
     * Get information about the remote version

69
     * @return bool|object

70
     */
71
    public function getRemote_information()
72
    {
73
    }
74
75
    /**

76
     * Return the status of the plugin licensing

77
     * @return boolean $remote_license

78
     */
79
    public function getRemote_license()
80
    {
81
    }
82
}

Schritt 2 Der Konstruktor

Der Konstruktor initiiert eine neue Instanz unserer Klasse. Es werden drei Parameter akzeptiert, die wir bereits im vorherigen Abschnitt definiert haben. Der Konstruktor legt die öffentlichen Eigenschaften "current_version", "update_path", "plugin_slug" und "slug" (die er aus den "plugin_slug" extrahiert) fest. Es werden auch die Filter "pre_set_site_transient_update_plugins" und "plugins_api" verknüpft.

1
/**

2
 * Initialize a new instance of the WordPress Auto-Update class

3
 * @param string $current_version

4
 * @param string $update_path

5
 * @param string $plugin_slug

6
 */
7
function __construct($current_version, $update_path, $plugin_slug)
8
{
9
	// Set the class public variables

10
	$this->current_version = $current_version;
11
	$this->update_path = $update_path;
12
	$this->plugin_slug = $plugin_slug;
13
	list ($t1, $t2) = explode('/', $plugin_slug);
14
	$this->slug = str_replace('.php', '', $t2);
15
16
	// define the alternative API for updating checking

17
	add_filter('pre_set_site_transient_update_plugins', array(&$this, 'check_update'));
18
19
	// Define the alternative response for information checking

20
	add_filter('plugins_api', array(&$this, 'check_info'), 10, 3);
21
}

Schritt 3 Fernanruffunktionen

Es gibt 3 Funktionen, die Remote-Anforderungen an unser selbst gehostetes Repository stellen. Es handelt sich um öffentliche Funktionen, die unabhängig voneinander verwendet werden können und von denen jede entweder den Antwortwert oder false zurückgibt.

getRemote_version - Gibt die neueste Plugin-Version aus unserem selbst gehosteten Repository als Zeichenfolge zurück.

1
/**

2
 * Return the remote version

3
 * @return bool|string $remote_version

4
 */
5
public function getRemote_version()
6
{
7
	$request = wp_remote_post($this->update_path, array('body' => array('action' => 'version')));
8
	if (!is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200) {
9
		return $request['body'];
10
	}
11
	return false;
12
}

getRemote_information - Gibt Informationen zur neuesten Version des Plugins aus unserem selbst gehosteten Repository als PHP-Objekt zurück.

1
/**

2
 * Get information about the remote version

3
 * @return bool|object

4
 */
5
public function getRemote_information()
6
{
7
	$request = wp_remote_post($this->update_path, array('body' => array('action' => 'info')));
8
	if (!is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200) {
9
		return unserialize($request['body']);
10
	}
11
	return false;
12
}

getRemote_license - Gibt den Lizenzstatus zurück. Diese Funktion ist optional und in diesem Lernprogramm ist keine Lizenzimplementierung vorhanden.

1
/**

2
 * Return the status of the plugin licensing

3
 * @return bool|string $remote_license

4
 */
5
public function getRemote_license()
6
{
7
	$request = wp_remote_post($this->update_path, array('body' => array('action' => 'license')));
8
	if (!is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200) {
9
		return $request['body'];
10
	}
11
	return false;
12
}

Schritt 4 Filterhaken

Die Klasse ist mit den Filtern "pre_set_site_transient_update_plugins" und "plugins_api" verknüpft. Eine ausführlichere Erläuterung der einzelnen Filter und des Hakens finden Sie im ersten Abschnitt, falls Sie ihn übersprungen haben.

1
/**

2
 * Add our self-hosted autoupdate plugin to the filter transient

3
 *

4
 * @param $transient

5
 * @return object $ transient

6
 */
7
public function check_update($transient)
8
{
9
	if (empty($transient->checked)) {
10
		return $transient;
11
	}
12
13
	// Get the remote version

14
	$remote_version = $this->getRemote_version();
15
16
	// If a newer version is available, add the update

17
	if (version_compare($this->current_version, $remote_version, '<')) {
18
		$obj = new stdClass();
19
		$obj->slug = $this->slug;
20
		$obj->new_version = $remote_version;
21
		$obj->url = $this->update_path;
22
		$obj->package = $this->update_path;
23
		$transient->response[$this->plugin_slug] = $obj;
24
	}
25
	return $transient;
26
}
27
28
/**

29
 * Add our self-hosted description to the filter

30
 *

31
 * @param boolean $false

32
 * @param array $action

33
 * @param object $arg

34
 * @return bool|object

35
 */
36
public function check_info($false, $action, $arg)
37
{
38
	if ($arg->slug === $this->slug) {
39
		$information = $this->getRemote_information();
40
		return $information;
41
	}
42
	return false;
43
}

Der vollständige Klassencode

1
class wp_auto_update
2
{
3
    /**

4
     * The plugin current version

5
     * @var string

6
     */
7
    public $current_version;
8
9
    /**

10
     * The plugin remote update path

11
     * @var string

12
     */
13
    public $update_path;
14
15
    /**

16
     * Plugin Slug (plugin_directory/plugin_file.php)

17
     * @var string

18
     */
19
    public $plugin_slug;
20
21
    /**

22
     * Plugin name (plugin_file)

23
     * @var string

24
     */
25
    public $slug;
26
27
    /**

28
     * Initialize a new instance of the WordPress Auto-Update class

29
     * @param string $current_version

30
     * @param string $update_path

31
     * @param string $plugin_slug

32
     */
33
    function __construct($current_version, $update_path, $plugin_slug)
34
    {
35
        // Set the class public variables

36
        $this->current_version = $current_version;
37
        $this->update_path = $update_path;
38
        $this->plugin_slug = $plugin_slug;
39
        list ($t1, $t2) = explode('/', $plugin_slug);
40
        $this->slug = str_replace('.php', '', $t2);
41
42
        // define the alternative API for updating checking

43
        add_filter('pre_set_site_transient_update_plugins', array(&$this, 'check_update'));
44
45
        // Define the alternative response for information checking

46
        add_filter('plugins_api', array(&$this, 'check_info'), 10, 3);
47
    }
48
49
    /**

50
     * Add our self-hosted autoupdate plugin to the filter transient

51
     *

52
     * @param $transient

53
     * @return object $ transient

54
     */
55
    public function check_update($transient)
56
    {
57
        if (empty($transient->checked)) {
58
            return $transient;
59
        }
60
61
        // Get the remote version

62
        $remote_version = $this->getRemote_version();
63
64
        // If a newer version is available, add the update

65
        if (version_compare($this->current_version, $remote_version, '<')) {
66
            $obj = new stdClass();
67
            $obj->slug = $this->slug;
68
            $obj->new_version = $remote_version;
69
            $obj->url = $this->update_path;
70
            $obj->package = $this->update_path;
71
            $transient->response[$this->plugin_slug] = $obj;
72
        }
73
        var_dump($transient);
74
        return $transient;
75
    }
76
77
    /**

78
     * Add our self-hosted description to the filter

79
     *

80
     * @param boolean $false

81
     * @param array $action

82
     * @param object $arg

83
     * @return bool|object

84
     */
85
    public function check_info($false, $action, $arg)
86
    {
87
        if ($arg->slug === $this->slug) {
88
            $information = $this->getRemote_information();
89
            return $information;
90
        }
91
        return false;
92
    }
93
94
    /**

95
     * Return the remote version

96
     * @return string $remote_version

97
     */
98
    public function getRemote_version()
99
    {
100
        $request = wp_remote_post($this->update_path, array('body' => array('action' => 'version')));
101
        if (!is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200) {
102
            return $request['body'];
103
        }
104
        return false;
105
    }
106
107
    /**

108
     * Get information about the remote version

109
     * @return bool|object

110
     */
111
    public function getRemote_information()
112
    {
113
        $request = wp_remote_post($this->update_path, array('body' => array('action' => 'info')));
114
        if (!is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200) {
115
            return unserialize($request['body']);
116
        }
117
        return false;
118
    }
119
120
    /**

121
     * Return the status of the plugin licensing

122
     * @return boolean $remote_license

123
     */
124
    public function getRemote_license()
125
    {
126
        $request = wp_remote_post($this->update_path, array('body' => array('action' => 'license')));
127
        if (!is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200) {
128
            return $request['body'];
129
        }
130
        return false;
131
    }
132
}

Server einrichten

Nachdem wir unser Plugin für selbst gehostete automatische Updates vorbereitet haben, ist es Zeit, den Server einzurichten, der die Benachrichtigung, die Beschreibung und das Update-Paket bereitstellt. In unserem Fall verwenden wir PHP mit einer einzelnen Datei namens "update.php" in meinem lokalen Server-Stammverzeichnis. Das Plugin-Update lautet "update.zip" im selben Verzeichnis.

Wenn Sie den Klassencode für die automatische Aktualisierung gelesen haben, werden Sie feststellen, dass drei Anforderungen ausgeführt werden, die von unserem API-Server verarbeitet werden sollten.

  1. Plugin Version
    • Anfragetyp: POST
    • Parameter/Wert: "action"/"version"
    • Rückgabewert: String ("1.0", "1.5", "2.0"...)
  2. Plugin Details
    • Anfragetyp: POST
    • Parameter/Wert: "action"/"Info"
    • Rückgabewert: String. Ein serialisiertes PHP widersprach
  3. Plugin-Lizenz
    • Anfragetyp: POST
    • Parameter/Wert: "action"/"license"
    • Rückgabewert: Fühlen Sie sich frei, es zu implementieren :)
  4. Plugin-Paket
    • Anfragetyp: GET
    • Parameter/Wert: keine
    • Rückgabewert: Zip-Paket

Code für die Datei update.php

1
if (isset($_POST['action'])) {
2
  switch ($_POST['action']) {
3
    case 'version':
4
      echo '1.1';
5
      break;
6
    case 'info':
7
      $obj = new stdClass();
8
      $obj->slug = 'plugin.php';
9
      $obj->plugin_name = 'plugin.php';
10
      $obj->new_version = '1.1';
11
      $obj->requires = '3.0';
12
      $obj->tested = '3.3.1';
13
      $obj->downloaded = 12540;
14
      $obj->last_updated = '2012-01-12';
15
      $obj->sections = array(
16
        'description' => 'The new version of the Auto-Update plugin',
17
        'another_section' => 'This is another section',
18
        'changelog' => 'Some new features'
19
      );
20
      $obj->download_link = 'http://localhost/update.php';
21
      echo serialize($obj);
22
    case 'license':
23
      echo 'false';
24
      break;
25
  }
26
} else {
27
    header('Cache-Control: public');
28
    header('Content-Description: File Transfer');
29
    header('Content-Type: application/zip');
30
    readfile('update.zip');
31
}

Anpassen der Plugin-Informationsbox

Es ist möglich, das Plugin-Informationsfeld anzupassen. Die Box hat:

  1. Informationen zum Plugin (Beschreibung, letzte Aktualisierung, Anzahl der Downloads)
  2. Abschnitte, in denen dem Informationsfeld Registerkarten hinzugefügt werden

Die Abschnitte können der Array-Eigenschaft "Abschnitte" hinzugefügt werden. Die Registerkarte "Beschreibung" ist selbst ein Abschnitt.

1
$obj->sections = array(
2
  'description' => 'The new version of the Auto-Update plugin',
3
  'another_section' => 'This is another section',
4
  'changelog' => 'Some new features'
5
);

Abschluss

Ich habe versucht, über den größten Teil des Aktualisierungsprozesses in diesem Tutorial zu sprechen. Der Quellcode für die Auto-Update-Klasse befindet sich in diesem GitHub-Repository. Fühlen Sie sich frei, Ihre Verbesserungen zu forken und voranzutreiben. Vorschläge, Fragen und Kritiken sind sicherlich willkommen.