File indexing completed on 2024-05-12 06:03:06

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Service
0018  * @subpackage Delicious
0019  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  * @version    $Id$
0022  */
0023 
0024 
0025 /**
0026  * @see Zend_Rest_Client
0027  */
0028 // require_once 'Zend/Rest/Client.php';
0029 
0030 /**
0031  * @see Zend_Json_Decoder
0032  */
0033 // require_once 'Zend/Json/Decoder.php';
0034 
0035 /**
0036  * @see Zend_Service_Delicious_SimplePost
0037  */
0038 // require_once 'Zend/Service/Delicious/SimplePost.php';
0039 
0040 /**
0041  * @see Zend_Service_Delicious_Post
0042  */
0043 // require_once 'Zend/Service/Delicious/Post.php';
0044 
0045 /**
0046  * @see Zend_Service_Delicious_PostList
0047  */
0048 // require_once 'Zend/Service/Delicious/PostList.php';
0049 
0050 /** @see Zend_Xml_Security */
0051 // require_once 'Zend/Xml/Security.php';
0052 
0053 /**
0054  * Zend_Service_Delicious is a concrete implementation of the del.icio.us web service
0055  *
0056  * @category   Zend
0057  * @package    Zend_Service
0058  * @subpackage Delicious
0059  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0060  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0061  */
0062 class Zend_Service_Delicious
0063 {
0064     const API_URI = 'https://api.del.icio.us';
0065 
0066     const PATH_UPDATE        = '/v1/posts/update';
0067     const PATH_TAGS          = '/v1/tags/get';
0068     const PATH_TAG_RENAME    = '/v1/tags/rename';
0069     const PATH_BUNDLES       = '/v1/tags/bundles/all';
0070     const PATH_BUNDLE_DELETE = '/v1/tags/bundles/delete';
0071     const PATH_BUNDLE_ADD    = '/v1/tags/bundles/set';
0072     const PATH_DATES         = '/v1/posts/dates';
0073     const PATH_POST_DELETE   = '/v1/posts/delete';
0074     const PATH_POSTS_GET     = '/v1/posts/get';
0075     const PATH_POSTS_ALL     = '/v1/posts/all';
0076     const PATH_POSTS_ADD     = '/v1/posts/add';
0077     const PATH_POSTS_RECENT  = '/v1/posts/recent';
0078 
0079     const JSON_URI     = 'http://del.icio.us';
0080     const JSON_POSTS   = '/feeds/json/%s/%s';
0081     const JSON_TAGS    = '/feeds/json/tags/%s';
0082     const JSON_NETWORK = '/feeds/json/network/%s';
0083     const JSON_FANS    = '/feeds/json/fans/%s';
0084     const JSON_URL     = '/feeds/json/url/data';
0085 
0086     /**
0087      * Zend_Service_Rest instance
0088      *
0089      * @var Zend_Service_Rest
0090      */
0091     protected $_rest;
0092 
0093     /**
0094      * Username
0095      *
0096      * @var string
0097      */
0098     protected $_authUname;
0099 
0100     /**
0101      * Password
0102      *
0103      * @var string
0104      */
0105     protected $_authPass;
0106 
0107     /**
0108      * Microtime of last request
0109      *
0110      * @var float
0111      */
0112     protected static $_lastRequestTime = 0;
0113 
0114     /**
0115      * Constructs a new del.icio.us Web Services Client
0116      *
0117      * @param  string $uname Client username
0118      * @param  string $pass  Client password
0119      * @return void
0120      */
0121     public function __construct($uname = null, $pass = null)
0122     {
0123         $this->_rest = new Zend_Rest_Client();
0124         $this->_rest->getHttpClient()->setConfig(array('ssltransport' => 'ssl'));
0125         $this->setAuth($uname, $pass);
0126     }
0127 
0128     /**
0129      * Set client username and password
0130      *
0131      * @param  string $uname Client user name
0132      * @param  string $pass  Client password
0133      * @return Zend_Service_Delicious Provides a fluent interface
0134      */
0135     public function setAuth($uname, $pass)
0136     {
0137         $this->_authUname = $uname;
0138         $this->_authPass  = $pass;
0139 
0140         return $this;
0141     }
0142 
0143     /**
0144      * Get time of the last update
0145      *
0146      * @throws Zend_Service_Delicious_Exception
0147      * @return Zend_Date
0148      */
0149     public function getLastUpdate()
0150     {
0151         $response = $this->makeRequest(self::PATH_UPDATE);
0152 
0153         $rootNode = $response->documentElement;
0154         if ($rootNode && $rootNode->nodeName == 'update') {
0155             /**
0156              * @todo replace strtotime() with Zend_Date equivalent
0157              */
0158             return new Zend_Date(strtotime($rootNode->getAttribute('time')));
0159         } else {
0160             /**
0161              * @see Zend_Service_Delicious_Exception
0162              */
0163             // require_once 'Zend/Service/Delicious/Exception.php';
0164             throw new Zend_Service_Delicious_Exception('del.icio.us web service has returned something odd!');
0165         }
0166     }
0167 
0168     /**
0169      * Get all tags, returning an array with tags as keys and number of corresponding posts as values
0170      *
0171      * @return array list of tags
0172      */
0173     public function getTags()
0174     {
0175         $response = $this->makeRequest(self::PATH_TAGS);
0176 
0177         return self::_xmlResponseToArray($response, 'tags', 'tag', 'tag', 'count');
0178     }
0179 
0180     /**
0181      * Rename a tag
0182      *
0183      * @param  string $old Old tag name
0184      * @param  string $new New tag name
0185      * @return Zend_Service_Delicious Provides a fluent interface
0186      */
0187     public function renameTag($old, $new)
0188     {
0189         $response = $this->makeRequest(self::PATH_TAG_RENAME, array('old' => $old, 'new' => $new));
0190 
0191         self::_evalXmlResult($response);
0192 
0193         return $this;
0194     }
0195 
0196     /**
0197      * Get all bundles, returning an array with bundles as keys and array of tags as values
0198      *
0199      * @return array list of bundles
0200      */
0201     public function getBundles()
0202     {
0203         $response = $this->makeRequest(self::PATH_BUNDLES);
0204 
0205         $bundles = self::_xmlResponseToArray($response, 'bundles', 'bundle', 'name', 'tags');
0206         foreach ($bundles as &$tags) {
0207             $tags = explode(' ', $tags);
0208         }
0209         return $bundles;
0210     }
0211 
0212     /**
0213      * Adds a new bundle
0214      *
0215      * @param  string $bundle Name of new bundle
0216      * @param  array  $tags   Array of tags
0217      * @return Zend_Service_Delicious Provides a fluent interface
0218      */
0219     public function addBundle($bundle, array $tags)
0220     {
0221         $tags = implode(' ', (array) $tags);
0222         $response = $this->makeRequest(self::PATH_BUNDLE_ADD, array('bundle' => $bundle, 'tags' => $tags));
0223 
0224         self::_evalXmlResult($response);
0225 
0226         return $this;
0227     }
0228 
0229     /**
0230      * Delete a bundle
0231      *
0232      * @param  string $bundle Name of bundle to be deleted
0233      * @return Zend_Service_Delicious Provides a fluent interface
0234      */
0235     public function deleteBundle($bundle)
0236     {
0237         $response = $this->makeRequest(self::PATH_BUNDLE_DELETE, array('bundle' => $bundle));
0238 
0239         self::_evalXmlResult($response);
0240 
0241         return $this;
0242     }
0243 
0244     /**
0245      * Delete a post
0246      *
0247      * @param  string $url URL of post to be deleted
0248      * @return Zend_Service_Delicious Provides a fluent interface
0249      */
0250     public function deletePost($url)
0251     {
0252         $response = $this->makeRequest(self::PATH_POST_DELETE, array('url' => $url));
0253 
0254         self::_evalXmlResult($response);
0255 
0256         return $this;
0257     }
0258 
0259     /**
0260      * Get number of posts by date
0261      *
0262      * Returns array where keys are dates and values are numbers of posts
0263      *
0264      * @param  string $tag Optional filtering by tag
0265      * @return array list of dates
0266      */
0267     public function getDates($tag = null)
0268     {
0269         $parms = array();
0270         if ($tag) {
0271             $parms['tag'] = $tag;
0272         }
0273 
0274         $response = $this->makeRequest(self::PATH_DATES, $parms);
0275 
0276         return self::_xmlResponseToArray($response, 'dates', 'date', 'date', 'count');
0277     }
0278 
0279     /**
0280      * Get posts matching the arguments
0281      *
0282      * If no date or url is given, most recent date will be used
0283      *
0284      * @param  string    $tag Optional filtering by tag
0285      * @param  Zend_Date $dt  Optional filtering by date
0286      * @param  string    $url Optional filtering by url
0287      * @throws Zend_Service_Delicious_Exception
0288      * @return Zend_Service_Delicious_PostList
0289      */
0290     public function getPosts($tag = null, Zend_Date $dt = null, $url = null)
0291     {
0292         $parms = array();
0293         if ($tag) {
0294             $parms['tag'] = $tag;
0295         }
0296         if ($url) {
0297             $parms['url'] = $url;
0298         }
0299         if ($dt) {
0300             $parms['dt'] = $dt->get('Y-m-d\TH:i:s\Z');
0301         }
0302 
0303         $response = $this->makeRequest(self::PATH_POSTS_GET, $parms);
0304 
0305         return $this->_parseXmlPostList($response);
0306     }
0307 
0308     /**
0309      * Get all posts
0310      *
0311      * @param  string $tag Optional filtering by tag
0312      * @return Zend_Service_Delicious_PostList
0313      */
0314     public function getAllPosts($tag = null)
0315     {
0316         $parms = array();
0317         if ($tag) {
0318             $parms['tag'] = $tag;
0319         }
0320 
0321         $response = $this->makeRequest(self::PATH_POSTS_ALL, $parms);
0322 
0323         return $this->_parseXmlPostList($response);
0324     }
0325 
0326     /**
0327      * Get recent posts
0328      *
0329      * @param  string $tag   Optional filtering by tag
0330      * @param  string $count Maximum number of posts to be returned (default 15)
0331      * @return Zend_Service_Delicious_PostList
0332      */
0333     public function getRecentPosts($tag = null, $count = 15)
0334     {
0335         $parms = array();
0336         if ($tag) {
0337             $parms['tag'] = $tag;
0338         }
0339         if ($count) {
0340             $parms['count'] = $count;
0341         }
0342 
0343         $response = $this->makeRequest(self::PATH_POSTS_RECENT, $parms);
0344 
0345         return $this->_parseXmlPostList($response);
0346     }
0347 
0348     /**
0349      * Create new post
0350      *
0351      * @return Zend_Service_Delicious_Post
0352      */
0353     public function createNewPost($title, $url)
0354     {
0355         return new Zend_Service_Delicious_Post($this, array('title' => $title, 'url' => $url));
0356     }
0357 
0358     /**
0359      * Get posts of a user
0360      *
0361      * @param  string $user  Owner of the posts
0362      * @param  int    $count Number of posts (default 15, max. 100)
0363      * @param  string $tag   Optional filtering by tag
0364      * @return Zend_Service_Delicious_PostList
0365      */
0366     public function getUserPosts($user, $count = null, $tag = null)
0367     {
0368         $parms = array();
0369         if ($count) {
0370             $parms['count'] = $count;
0371         }
0372 
0373         $path = sprintf(self::JSON_POSTS, $user, $tag);
0374         $res = $this->makeRequest($path, $parms, 'json');
0375 
0376         return new Zend_Service_Delicious_PostList($this, $res);
0377     }
0378 
0379     /**
0380      * Get tags of a user
0381      *
0382      * Returned array has tags as keys and number of posts as values
0383      *
0384      * @param  string $user    Owner of the posts
0385      * @param  int    $atleast Include only tags for which there are at least ### number of posts
0386      * @param  int    $count   Number of tags to get (default all)
0387      * @param  string $sort    Order of returned tags ('alpha' || 'count')
0388      * @return array
0389      */
0390     public function getUserTags($user, $atleast = null, $count = null, $sort = 'alpha')
0391     {
0392         $parms = array();
0393         if ($atleast) {
0394             $parms['atleast'] = $atleast;
0395         }
0396         if ($count) {
0397             $parms['count'] = $count;
0398         }
0399         if ($sort) {
0400             $parms['sort'] = $sort;
0401         }
0402 
0403         $path = sprintf(self::JSON_TAGS, $user);
0404 
0405         return $this->makeRequest($path, $parms, 'json');
0406     }
0407 
0408     /**
0409      * Get network of a user
0410      *
0411      * @param  string $user Owner of the network
0412      * @return array
0413      */
0414     public function getUserNetwork($user)
0415     {
0416         $path = sprintf(self::JSON_NETWORK, $user);
0417         return $this->makeRequest($path, array(), 'json');
0418     }
0419 
0420     /**
0421      * Get fans of a user
0422      *
0423      * @param  string $user Owner of the fans
0424      * @return array
0425      */
0426     public function getUserFans($user)
0427     {
0428         $path = sprintf(self::JSON_FANS, $user);
0429         return $this->makeRequest($path, array(), 'json');
0430     }
0431 
0432     /**
0433      * Get details on a particular bookmarked URL
0434      *
0435      * Returned array contains four elements:
0436      *  - hash - md5 hash of URL
0437      *  - top_tags - array of tags and their respective usage counts
0438      *  - url - URL for which details were returned
0439      *  - total_posts - number of users that have bookmarked URL
0440      *
0441      * If URL hasen't been bookmarked null is returned.
0442      *
0443      * @param  string $url URL for which to get details
0444      * @return array
0445      */
0446     public function getUrlDetails($url)
0447     {
0448         $parms = array('hash' => md5($url));
0449 
0450         $res = $this->makeRequest(self::JSON_URL, $parms, 'json');
0451 
0452         if(isset($res[0])) {
0453             return $res[0];
0454         } else {
0455             return null;
0456         }
0457     }
0458 
0459     /**
0460      * Handles all GET requests to a web service
0461      *
0462      * @param   string $path  Path
0463      * @param   array  $parms Array of GET parameters
0464      * @param   string $type  Type of a request ("xml"|"json")
0465      * @return  mixed  decoded response from web service
0466      * @throws  Zend_Service_Delicious_Exception
0467      */
0468     public function makeRequest($path, array $parms = array(), $type = 'xml')
0469     {
0470         // if previous request was made less then 1 sec ago
0471         // wait until we can make a new request
0472         $timeDiff = microtime(true) - self::$_lastRequestTime;
0473         if ($timeDiff < 1) {
0474             usleep((1 - $timeDiff) * 1000000);
0475         }
0476 
0477         $this->_rest->getHttpClient()->setAuth($this->_authUname, $this->_authPass);
0478 
0479         switch ($type) {
0480             case 'xml':
0481                 $this->_rest->setUri(self::API_URI);
0482                 break;
0483             case 'json':
0484                 $parms['raw'] = true;
0485                 $this->_rest->setUri(self::JSON_URI);
0486                 break;
0487             default:
0488                 /**
0489                  * @see Zend_Service_Delicious_Exception
0490                  */
0491                 // require_once 'Zend/Service/Delicious/Exception.php';
0492                 throw new Zend_Service_Delicious_Exception('Unknown request type');
0493         }
0494 
0495         self::$_lastRequestTime = microtime(true);
0496         $response = $this->_rest->restGet($path, $parms);
0497 
0498         if (!$response->isSuccessful()) {
0499             /**
0500              * @see Zend_Service_Delicious_Exception
0501              */
0502             // require_once 'Zend/Service/Delicious/Exception.php';
0503             throw new Zend_Service_Delicious_Exception("Http client reported an error: '{$response->getMessage()}'");
0504         }
0505 
0506         $responseBody = $response->getBody();
0507 
0508         switch ($type) {
0509             case 'xml':
0510                 $dom = new DOMDocument() ;
0511     
0512                 if (!$dom = @Zend_Xml_Security::scan($responseBody, $dom)) {
0513                     /**
0514                      * @see Zend_Service_Delicious_Exception
0515                      */
0516                     // require_once 'Zend/Service/Delicious/Exception.php';
0517                     throw new Zend_Service_Delicious_Exception('XML Error');
0518                 }
0519 
0520                 return $dom;
0521             case 'json':
0522                 return Zend_Json_Decoder::decode($responseBody);
0523         }
0524     }
0525 
0526     /**
0527      * Transform XML string to array
0528      *
0529      * @param   DOMDocument $response
0530      * @param   string      $root     Name of root tag
0531      * @param   string      $child    Name of children tags
0532      * @param   string      $attKey   Attribute of child tag to be used as a key
0533      * @param   string      $attValue Attribute of child tag to be used as a value
0534      * @return  array
0535      * @throws  Zend_Service_Delicious_Exception
0536      */
0537     private static function _xmlResponseToArray(DOMDocument $response, $root, $child, $attKey, $attValue)
0538     {
0539         $rootNode = $response->documentElement;
0540         $arrOut = array();
0541 
0542         if ($rootNode->nodeName == $root) {
0543             $childNodes = $rootNode->childNodes;
0544 
0545             for ($i = 0; $i < $childNodes->length; $i++) {
0546                 $currentNode = $childNodes->item($i);
0547                 if ($currentNode->nodeName == $child) {
0548                     $arrOut[$currentNode->getAttribute($attKey)] = $currentNode->getAttribute($attValue);
0549                 }
0550             }
0551         } else {
0552             /**
0553              * @see Zend_Service_Delicious_Exception
0554              */
0555             // require_once 'Zend/Service/Delicious/Exception.php';
0556             throw new Zend_Service_Delicious_Exception('del.icio.us web service has returned something odd!');
0557         }
0558 
0559         return $arrOut;
0560     }
0561 
0562     /**
0563      * Constructs Zend_Service_Delicious_PostList from XML response
0564      *
0565      * @param   DOMDocument $response
0566      * @return  Zend_Service_Delicious_PostList
0567      * @throws  Zend_Service_Delicious_Exception
0568      */
0569     private function _parseXmlPostList(DOMDocument $response)
0570     {
0571         $rootNode = $response->documentElement;
0572 
0573         if ($rootNode->nodeName == 'posts') {
0574             return new Zend_Service_Delicious_PostList($this, $rootNode->childNodes);
0575         } else {
0576             /**
0577              * @see Zend_Service_Delicious_Exception
0578              */
0579             // require_once 'Zend/Service/Delicious/Exception.php';
0580             throw new Zend_Service_Delicious_Exception('del.icio.us web service has returned something odd!');
0581         }
0582     }
0583 
0584     /**
0585      * Evaluates XML response
0586      *
0587      * @param   DOMDocument $response
0588      * @return  void
0589      * @throws  Zend_Service_Delicious_Exception
0590      */
0591     private static function _evalXmlResult(DOMDocument $response)
0592     {
0593         $rootNode = $response->documentElement;
0594 
0595         if ($rootNode && $rootNode->nodeName == 'result') {
0596 
0597             if ($rootNode->hasAttribute('code')) {
0598                 $strResponse = $rootNode->getAttribute('code');
0599             } else {
0600                 $strResponse = $rootNode->nodeValue;
0601             }
0602 
0603             if ($strResponse != 'done' && $strResponse != 'ok') {
0604                 /**
0605                  * @see Zend_Service_Delicious_Exception
0606                  */
0607                 // require_once 'Zend/Service/Delicious/Exception.php';
0608                 throw new Zend_Service_Delicious_Exception("del.icio.us web service: '{$strResponse}'");
0609             }
0610         } else {
0611             /**
0612              * @see Zend_Service_Delicious_Exception
0613              */
0614             // require_once 'Zend/Service/Delicious/Exception.php';
0615             throw new Zend_Service_Delicious_Exception('del.icio.us web service has returned something odd!');
0616         }
0617     }
0618 }