Le protocole Webdav permet d'accéder en lecture/écriture à une arborescence de fichiers au travers du protocole HTTP.
Généralement on utilise ce protocole pour accéder un système de fichier distant.
De la même manière que l'on peut produire une page HTML sans qu'elle existe réellement sur le serveur HTTP, on peut simuler un système de fichier complet en Webdav. Comme pour générer du HTML on peut utiliser PHP pour produire le XML spécifique à Webdav. Pour éviter d'apprendre la norme Webdav, on peut utiliser des APIs. A ma connaissance, il existe 2 en PHP :
Installation
Package PEAR non à jour
Bien que emballée sous forme d'un package PEAR, il est préférable de ne pas utiliser cette version. Le code source de la classe a évolué mais le package PEAR, lui, n'a jamais été régénéré. Il donc préférable d'aller chercher directement le code source dans SVN.
Depuis le SVN
Cette classe est très légère (3 fichiers). Donc pour l'utiliser, on peut récupérer à la main ces 3 fichiers (sans même passer par les commandes subversion) :
% mkdir -p HTTP/WebDAV/Tools/ % cd HTTP/WebDAV/ % wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Server.php?view=co -O Server.php % cd Tools % wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Tools/_parse_lockinfo.php?view=co -O _parse_lockinfo.php % wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Tools/_parse_propfind.php?view=co -O _parse_propfind.php % wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Tools/_parse_proppatch.php?view=co -O _parse_proppatch.php
Environnement de Test
Serveur HTTP
Pour utiliser notre classe, il faut un serveur HTTP. Pour plus de souplesse dans la configuration, on peut utiliser PkgiFichiers
En plus des fichiers téléchargés, il nous faut 3 autres fichiers :
- .htaccess
- index.php
- Test.php
Ce qui donne
`-----.htaccess `-----index.php `-----HTTP `-----WebDAV `-----Server.php `-----Server | `-----Test.php `-----Tools `-----_parse_lockinfo.php `-----_parse_propfind.php `-----_parse_proppatch.php
Configuration
Un serveur Webdav s'attend à répondre à des URLs pointant vers des répertoires et des fichiers sans aucun paramètre supplémentaire. Donc pour concentrer les accès (toutes les URLS) vers un point unique on utilisera le module Apache mod_rewrite que l'on configura avec ces instructions dans le fichier .htaccess :
<IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php </IfModule>Le fichier index.php lance le serveur webdav.
<?php _SERVER['SCRIPT_NAME'] = ''; $server->ServeRequest();
La classe HTTP_WebDAV_Server_Test
La classe HTTP_WebDAV_Server s'utilise par dérivation, il faut donc la surcharger et coder les méthodes abstraites. A chaque méthode correspond (en gros) une action webdav :
- GET() get a resource from the server
- HEAD() get resource headers only from the server
- PUT() create or modify a resource on the server
- COPY() copy a resource on the server
- MOVE() move a resource on the server
- DELETE() delete a resource on the server
- MKCOL() create a new collection
- PROPFIND() get property data for a resource
- PROPPATCH() modify property data for a resource
- LOCK() lock a resource
- UNLOCK() unlock a locked resource
- checklock() check whether a resource is locked
- check_auth() check authentication
require_once "HTTP/WebDAV/Server.php"; class HTTP_WebDAV_Server_Test extends HTTP_WebDAV_Server { var $myfile = ''; function check_auth($type, $user, $pass) { return true; } function HEAD(&$options) { $options['mimetype'] = 'text/xml'; $options['mtime'] = filemtime(__FILE__); $options['size'] = strlen(sprintf($this->myfile, basename($options['path']), $options['path'])); return true; } function GET(&$options) { if (!$this->HEAD($options)) { return false; } $options['data'] = sprintf($this->myfile, basename($options['path']), $options['path']); return true; } function PROPFIND(&$options, &$files) { $i = 0; $files["files"] = array(); // On décrit la ressource demandée $path = $options['path']; $name = basename($path); $files["files"][$i] = array(); $files["files"][$i]["path"] = $path; $files["files"][$i]["props"] = array(); $files["files"][$i]["props"][] = $this->mkprop("displayname", $name); $files["files"][$i]["props"][] = $this->mkprop("creationdate", filectime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("getlastmodified", filemtime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("lastaccessed", fileatime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("ishidden", false); // La ressource demandée est-elle un repertoire ou un fichier ? $testdir = trim($options['path'], '/'); if ($testdir === '' or preg_match(',MyDir$,', $testdir)) { $files["files"][$i]["props"][] = $this->mkprop("resourcetype", "collection"); $files["files"][$i]["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory"); } else { $files["files"][$i]["props"][] = $this->mkprop("getcontenttype", 'text/xml'); $files["files"][$i]["props"][] = $this->mkprop("resourcetype", ""); $files["files"][$i]["props"][] = $this->mkprop("getcontentlength", strlen(sprintf($this->myfile, $name, $path))); } ++$i; // On décrit le contenu de la ressource if ($options['depth'] == 1) { $path = rtrim($options['path'], '/').'/MyDir'; $name = basename($path); $files["files"][$i] = array(); $files["files"][$i]["path"] = $path; $files["files"][$i]["props"] = array(); $files["files"][$i]["props"][] = $this->mkprop("displayname", $name); $files["files"][$i]["props"][] = $this->mkprop("creationdate", filectime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("getlastmodified", filemtime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("lastaccessed", fileatime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("resourcetype", "collection"); $files["files"][$i]["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory"); ++$i; $path = rtrim($options['path'], '/').'/MyFile.xml'; $name = basename($path); $files["files"][$i] = array(); $files["files"][$i]["path"] = $path; $files["files"][$i]["props"] = array(); $files["files"][$i]["props"][] = $this->mkprop("displayname", $name); $files["files"][$i]["props"][] = $this->mkprop("creationdate", filectime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("getlastmodified", filemtime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("lastaccessed", fileatime(__FILE__)); $files["files"][$i]["props"][] = $this->mkprop("ishidden", false); $files["files"][$i]["props"][] = $this->mkprop("getcontenttype", 'text/xml'); $files["files"][$i]["props"][] = $this->mkprop("resourcetype", ""); $files["files"][$i]["props"][] = $this->mkprop("getcontentlength", strlen(sprintf($this->myfile, $name, $path))); ++$i; } return true; } } %s %s
Pour tester
En ligne de commande
L'utilitaire cadaver permet de tester toutes les parties du protocoles Webdav.
% cadaver dav:!> open http://localhost:50009/ dav:/> ls Listing collection `/': succeeded. Coll: MyDir 0 juil. 15 15:35 MyFile.xml 60 juil. 15 15:35 dav:/> cd MyDir dav:/MyDir/> ls Listing collection `/MyDir/': succeeded. Coll: MyDir 0 juil. 15 15:35 MyFile.xml 66 juil. 15 15:35 dav:/MyDir/> get MyFile.xml Downloading `/MyDir/MyFile.xml' to MyFile.xml: Progress: [=============================>] 100,0% of 66 bytes succeeded. dav:/MyDir/> propget MyFile.xml Fetching properties for `MyFile.xml': displayname = MyFile.xml creationdate = 2009-07-15T13:35:11Z getlastmodified = Wed, 15 Jul 2009 13:35:11 GMT lastaccessed = Wed, 15 Jul 2009 13:39:15 GMT ishidden = getcontenttype = text/xml resourcetype = getcontentlength = 66 dav:/MyDir/> more MyFile.xml Displaying `/MyDir/MyFile.xml':dav:/MyDir/> exit Connection to `localhost' closed. % MyFile.xml /MyDir/MyFile.xml
J'encourage vraiment cet article qui est quasiment l'unique sur la toile concernant cette classe. Le protocole webdav est formidable et il est temps de le démocratiser.
RépondreSupprimerL'article semble encore incomplet, et sans code supplémentaire il est impossible de faire fonctionner l'exemple. Ce serait pas mal d'en avoir un peu plus.
Merci pour l'encouragement.Par faute de temps, j'ai effectivement un peu laissé l'article tel quel...
RépondreSupprimer