]> git.mxchange.org Git - friendica.git/blob - library/slinky.php
fix typo
[friendica.git] / library / slinky.php
1 <?php
2 /*
3 Copyright (c) 2009, Beau Lebens
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without modification,
7 are permitted provided that the following conditions are met:
8
9  - Redistributions of source code must retain the above copyright notice, this 
10    list of conditions and the following disclaimer.
11  - Redistributions in binary form must reproduce the above copyright notice, 
12    this list of conditions and the following disclaimer in the documentation 
13    and/or other materials provided with the distribution.
14  - Neither the name of Dented Reality nor the names of the authors may be used 
15    to endorse or promote products derived from this software without specific 
16    prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND     ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28         
29 */
30
31 // Return options for Slinky_Service->url_get() and ->url_post()
32 define( 'SLINKY_BODY', 1 ); // Default
33 define( 'SLINKY_HEADERS', 2 ); // Not implemented yet
34 define( 'SLINKY_FINAL_URL', 3 ); // Default for lengthening URLs
35
36 // So that services may decide what to do with us
37 define( 'SLINKY_USER_AGENT', 'Slinky v1.0 +http://dentedreality.com.au/projects/slinky/' );
38
39 // How many seconds until remote requests should be cut?
40 define( 'SLINKY_TIMEOUT', 10 );
41
42 /**
43  * Slinky allows you to go back and forth between "long" and shortened URLs 
44  * using popular URL shortening services.
45  * 
46  * Slinky assumes you have cURL installed and working, and requires the JSON
47  * extension installed if you're working with a service that uses JSON.
48  * 
49  * Slinky will ONLY work with PHP5+
50  * 
51  * It supports some of the more popular services, with easy extensibility for
52  * adding your own relatively easily. It defaults to using TinyURL
53  * for shortening URLs. If you want to use some of the other services, you need
54  * to set some configuration options before actually shortening/lengthening
55  * URLs. I'd strongly suggest that you cache results using memcached, a local
56  * DB or whatever to avoid having to hit APIs etc every time you encounter a
57  * URL.
58  * 
59  * Slinky supports shortening, and auto-detection (for lengthening URLs) 
60  * using these services:
61  * - Bit.ly
62  * - Tr.im
63  * - TinyURL
64  * - Is.Gd
65  * - Fon.gs
66  * - Micurl.com
67  * - ur1.ca
68  * - Ptiturl
69  * - Tighturl
70  * - 2tu.us
71  * - Snipr / Snipurl / Snurl.com / Sn.im
72  * 
73  * 
74  * To use Slinky:
75  * 
76  * $slinky = new Slinky( 'http://dentedreality.com.au/' );
77  * - Creates a new Slinky instance, will default to using TinyURL for ->short();
78  * 
79  * $slinky = new Slinky( 'http://dentedreality.com.au', new Slinky_Bitly() );
80  * - Creates new Slinky, forces use of Bit.ly for ->short();
81  * 
82  * $slinky = new Slinky( 'http://dentedreality.com.au/' );
83  * echo $slinky->short();
84  * - echos the short version of http://dentedreality.com.au/ (default to TinyURL)
85  * 
86  * $slinky = new Slinky( 'http://tinyurl.com/jw5sh' );
87  * echo $slinky->long();
88  * - echos out the long version of http://tinyurl.com/jw5sh (which should be http://dentedreality.com.au/)
89  * 
90  * $slinky = new Slinky( 'http://dentedreality.com.au/' );
91  * echo $slinky->long();
92  * - Attempts to lengthen the URL, but will not auto-detect which service it is
93  *   so it will just output the original URL. Useful for always attempting to
94  *   lengthen any URL you come across (fails gracefully)
95  * 
96  * $slinky = new Slinky( 'http://dentedreality.com.au/' );
97  * $slinky->set_cascade( array( new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
98  * echo $slinky->short();
99  * - Uses the powerful cascade mode to make sure that we get a short URL from 
100  *   Tr.im, Is.Gd or TinyURL (in that order).
101  * 
102  * See specific service class definitions below for examples of how to use them,
103  * as some services allow (or require) additional properties before you can use
104  * them (for authentication etc).
105  * 
106  * To use a different service with Slinky, just create your own class and
107  * extend Slinky_Service(). Make sure you implement url_is_short(), url_is_long(),
108  * make_short() and make_long(). If you need to GET or POST a URL, you can use
109  * ->url_get() and ->url_post(), which your class will have inherited.
110 **/
111 class Slinky {
112         var $url     = false;
113         var $service = false;
114         var $cascade = false;
115         
116         function __construct( $url = false, $service = false ) {
117                 $this->url     = $url;
118                 $this->service = $service;
119         }
120         
121         /**
122          * Specify which URL Service to use
123          *
124          * @param Slinky_Service $service Packaged or custom Service object
125          * @return void
126          */
127         public function set_service( $service = false ) {
128                 if ( is_object( $service ) ) {
129                         $this->service = $service;
130                 }
131         }
132         
133         /**
134          * If you pass an array of Slinky_Service objects to this method, they will
135          * be used in order to try to get a short URL, so if one fails, it will
136          * try the next and so on, until it gets a valid short URL, or it runs
137          * out of options.
138          * 
139          * @param array $services List of Slinky_Service objects as an array
140         **/
141         public function set_cascade( $services = false ) {
142                 if ( !$services || !is_array( $services ) )
143                         return false;
144                 
145                 $this->cascade = $services;
146         }
147         
148         /**
149          * Guess the URL service to use from known domains of short URLs
150          *
151          * @param string $url 
152          */
153         public function set_service_from_url( $url = false ) {
154                 if ( !$url )
155                         $url = $this->url;
156                 
157                 $host = parse_url( $url, PHP_URL_HOST );
158                 switch ( str_replace( 'www.', '', $host ) ) {
159                         case 'bit.ly':
160                                 if ( class_exists( 'Slinky_Bitly' ) ) {
161                                         $this->service = new Slinky_Bitly();
162                                         break;
163                                 }
164                         case 'tr.im':
165                                 if ( class_exists( 'Slinky_Trim' ) ) {
166                                         $this->service = new Slinky_Trim();
167                                         break;
168                                 }
169                         case 'tinyurl.com':
170                                 if ( class_exists( 'Slinky_TinyURL' ) ) {
171                                         $this->service = new Slinky_TinyURL();
172                                         break;
173                                 }
174                         case 'is.gd':
175                                 if ( class_exists( 'Slinky_IsGd' ) ) {
176                                         $this->service = new Slinky_IsGd();
177                                         break;
178                                 }
179                         case 'fon.gs':
180                                 if ( class_exists( 'Slinky_Fongs' ) ) {
181                                         $this->service = new Slinky_Fongs();
182                                         break;
183                                 }
184                         case 'micurl.com':
185                                 if ( class_exists( 'Slinky_Micurl' ) ) {
186                                         $this->service = new Slinky_Micurl();
187                                         break;
188                                 }
189                         case 'ur1.ca':
190                                 if ( class_exists( 'Slinky_Ur1ca' ) ) {
191                                         $this->service = new Slinky_Ur1ca();
192                                         break;
193                                 }
194                         case 'ptiturl.com':
195                                 if ( class_exists( 'Slinky_PtitURL' ) ) {
196                                         $this->service = new Slinky_PtitURL();
197                                         break;
198                                 }
199                         case 'tighturl.com':
200                         case '2tu.us':
201                                 if ( class_exists( 'Slinky_TightURL' ) ) {
202                                         $this->service = new Slinky_TightURL();
203                                         break;
204                                 }
205                         case 'snipr.com':
206                         case 'snipurl.com':
207                         case 'snurl.com':
208                         case 'sn.im':
209                                 if ( class_exists( 'Slinky_Snipr' ) ) {
210                                         $this->service = new Slinky_Snipr();
211                                         break;
212                                 }
213                         default:
214                                 $this->service = new Slinky_Default();
215                                 break;
216                 }
217         }
218         
219         /**
220          * Take a long URL and make it short. Will avoid "re-shortening" a URL if it
221          * already seems to be short.
222          *
223          * @param string $url Optional URL to shorten, otherwise use $this->url
224          * @return The short version of the URL
225          */
226         public function short( $url = false ) {
227                 if ( $url )
228                         $this->url = $url;
229                         
230                 if ( !$this->service )
231                         $this->set_service( new Slinky_TinyURL() ); // Defaults to tinyurl because it doesn't require any configuration
232                 
233                 if ( !$this->cascade )
234                         $this->cascade = array( $this->service ); // Use a single service in cascade mode
235                 
236                 foreach ( $this->cascade as $service ) {
237                         if ( $service->url_is_short( $this->url ) )
238                                 return trim( $this->url ); // Identified as already short, using this service
239
240                         $response = trim( $service->make_short( $this->url ) );
241                         if ( $response && $this->url != $response )
242                                 return trim( $response );
243                 }
244                 
245                 return $this->url; // If all else fails, just send back the URL we already know about
246         }
247         
248         /**
249          * Take a short URL and make it long ("resolve" it).
250          *
251          * @param string $url The short URL
252          * @return A long URL
253          */
254         public function long( $url = false ) {
255                 if ( $url )
256                         $this->url = $url;
257                         
258                 if ( !$this->service )
259                         $this->set_service_from_url();
260                 
261                 if ( $this->service->url_is_long( $this->url ) )
262                         return trim( $this->url );
263                 
264                 return trim( $this->service->make_long( $this->url ) );
265         }
266 }
267
268 /**
269  * Use this class to create a Service implementation for your own URL 
270  * shortening service. Extend the class and customize methods to suit your 
271  * service. Note that it is an "abstract" class, so there are certain methods 
272  * which you *must* define.
273 **/
274 abstract class Slinky_Service {
275         
276         /**
277          * Determine, based on the input URL, if it's already a short URL, from
278          * this particular service. e.g. a Bit.ly URL contains "bit.ly/"
279         **/
280         abstract function url_is_short( $url );
281         
282         /**
283          * Determine if this is a "long" URL (just means it hasn't been shortened)
284          * already. e.g. a no-Bit.ly URL would NOT contain "bit.ly/"
285         **/
286         abstract function url_is_long( $url );
287         
288         /**
289          * Handle taking the $url and converting it to a short URL via whatever
290          * means is provided at the remote service.
291         **/
292         abstract function make_short( $url );
293         
294         /**
295          * Return the long/expanded version of a URL via any API means available
296          * from this service. As a fallback, you might
297          * consider just following the URL and using SLINKY_FINAL_URL as the 
298          * return method from a $this->url_get() call to find out.
299          * 
300          * This one is optional for Services extending this class, if they don't
301          * then the following implementation will work on most services anyway.
302         **/
303         public function make_long( $url ) {
304                 return $this->url_get( $url, SLINKY_FINAL_URL );
305         }
306         
307         /**
308          * Method for getting properties that you might need during the process
309          * of shortening/lengthening a URL (e.g. auth credentials)
310         **/
311         public function get( $prop ) {
312                 if ( empty( $this->$prop ) )
313                         return null;
314                         
315                 return $this->$prop;
316         }
317         
318         /**
319          * Method for setting properties that you might need during the process
320          * of shortening/lengthening a URL (e.g. auth credentials)
321         **/
322         public function set( $prop, $val ) {
323                 $this->$prop = $val;
324         }
325         
326         /**
327          * Internal helper for performing a GET request on a remote URL.
328          * 
329          * @param string $url The URL to GET
330          * @param const $return The return method [ SLINKY_BODY | SLINKY_FINAL_URL | SLINKY_HEADERS ]
331          * @return Mixed, based on the $return var passed in.
332         **/
333         protected function url_get( $url, $return = SLINKY_BODY ) {
334                 $ch = curl_init( $url );
335                 curl_setopt( $ch, CURLOPT_USERAGENT, SLINKY_USER_AGENT );
336                 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );                  // Don't stress about SSL validity
337                 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );                  // Return the response, don't output it
338                 curl_setopt( $ch, CURLOPT_TIMEOUT, SLINKY_TIMEOUT );    // Limit how long we'll wait for a response
339                 curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );                  // Allow following of redirections
340                 $r  = curl_exec( $ch );
341                 if ( curl_errno( $ch ) ) {
342                         return false;
343                 }
344                 
345                 // Return whatever we were asked for
346                 if ( SLINKY_FINAL_URL == $return )
347                         return curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL );
348                 else if ( SLINKY_BODY == $return )
349                         return $r;
350                 
351                 return false;
352         }
353         
354         /**
355          * Internal helper for performing a POST request on a remote URL.
356          * 
357          * @param string $url The URL to POST to
358          * @param array $payload Array containing name/value pairs of the parameters to POST
359          * @param const $return The return method [ SLINKY_BODY | SLINKY_FINAL_URL | SLINKY_HEADERS ]
360          * @return Mixed, based on the $return var passed in.
361         **/
362         protected function url_post( $url, $payload = array(), $return = SLINKY_BODY ) {
363                 $ch = curl_init( $url );
364                 curl_setopt( $ch, CURLOPT_POST, true );
365                 curl_setopt( $ch, CURLOPT_POSTFIELDS, (array) $payload );
366                 curl_setopt( $ch, CURLOPT_USERAGENT, SLINKY_USER_AGENT );
367                 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );                  // Don't stress about SSL validity
368                 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );                  // Return the response, don't output it
369                 curl_setopt( $ch, CURLOPT_TIMEOUT, SLINKY_TIMEOUT );    // Limit how long we'll wait for a response
370                 curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );                  // Allow following of redirections
371                 $r  = curl_exec( $ch );
372                 if ( curl_errno( $ch ) ) {
373                         return false;
374                 }
375                 
376                 // Return whatever we were asked for
377                 if ( SLINKY_FINAL_URL == $return )
378                         return curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL );
379                 else if ( SLINKY_BODY == $return )
380                         return $r;
381                 
382                 return false;
383         }
384 }
385
386 // This default service is used in cases when you try to do something based
387 // on auto-detection, but we can't detect anything. It will also resolve URLs
388 // to their "long" version by following all redirects.
389 class Slinky_Default extends Slinky_Service {
390         function url_is_short( $url ) {
391                 return false;
392         }
393         
394         function url_is_long( $url ) {
395                 return false;
396         }
397         
398         function make_short( $url ) {
399                 return $url;
400         }
401 }
402
403 // Implementation of TinyURL as a Slinky Service
404 class Slinky_TinyURL extends Slinky_Service {
405         function url_is_short( $url ) {
406                 return stristr( $url, 'tinyurl.com/' );
407         }
408         
409         function url_is_long( $url ) {
410                 return !stristr( $url, 'tinyurl.com/' );
411         }
412         
413         function make_short( $url ) {
414                 return $this->url_get( 'http://tinyurl.com/api-create.php?url=' . urlencode( $url ) );
415         }
416         
417         function make_long( $url ) {
418                 $bits = parse_url( $url );
419                 $result = $this->url_get( 'http://tinyurl.com/preview.php?num=' . substr( $bits['path'], 1 ) );
420                 if ( preg_match('/<a id="redirecturl" href="([^"]+)">/is', $result, $matches ) )
421                         return $matches[1];
422                 else
423                         return $url;
424         }
425 }
426
427 // Implementation of Bit.ly as a Slinky Service
428 /*
429 To use Bit.ly, you MUST set your login and apiKey for the service first, e.g.
430
431 $bitly = new Slinky_Bitly();
432 $bitly->set( 'login', 'bitly_login' );
433 $bitly->set( 'apiKey', 'bitly_apiKey' );
434
435 $slinky = new Slinky( $url, $bitly );
436 echo $slinky->short();
437
438 You could also do this if the URL was already a bit.ly URL and you 
439 were going to make it longer, since Bitly is supported for auto-detection:
440
441 $slinky = new Slinky( $url );
442 $slinky->set_service_from_url();
443 $slinky->service->set( 'login', 'bitly_login' );
444 $slinky->service->set( 'apiKey', 'bitly_apiKey' );
445 echo $slinky->long();
446
447 */
448 class Slinky_Bitly extends Slinky_Service {
449         function url_is_short( $url ) {
450                 return stristr( $url, 'bit.ly/' );
451         }
452         
453         function url_is_long( $url ) {
454                 return !stristr( $url, 'bit.ly/' );
455         }
456         
457         function make_short( $url ) {
458                 // Can't do anything unless these 2 properties are set first
459                 if ( !$this->get( 'login' ) || !$this->get( 'apiKey' ) )
460                         return $url;
461                 
462                 $result = $this->url_post( 'http://api.bit.ly/shorten?version=2.0.1&format=json&login=' . $this->get( 'login' ) . '&apiKey=' . $this->get( 'apiKey' ) . '&longUrl=' . urlencode( $url ) );
463                 $result = json_decode( $result );
464                 if ( !$result->errorCode ) {
465                         foreach ( $result->results as $detail ) {
466                                 return $detail->shortUrl;
467                         }
468                 } else {
469                         return false;
470                 }
471         }
472         
473         function make_long( $url ) {
474                 // Can't do anything unless these 2 properties are set first
475                 if ( !$this->get( 'login' ) || !$this->get( 'apiKey' ) )
476                         return $url;
477                 
478                 $result = $this->url_post( 'http://api.bit.ly/expand?version=2.0.1&format=json&login=' . $this->get( 'login' ) . '&apiKey=' . $this->get( 'apiKey' ) . '&shortUrl=' . urlencode( $url ) );
479                 $result = json_decode( $result );
480                 if ( !$result->errorCode ) {
481                         foreach ( $result->results as $detail ) {
482                                 return $detail->longUrl;
483                         }
484                 } else {
485                         return false;
486                 }
487         }
488 }
489
490 // Implementation of Tr.im as a Slinky Service
491 /*
492 When using Tr.im, you MAY optionally set your username and password to tie 
493 URLs to your account, e.g.
494
495 $trim = new Slinky_Trim();
496 $trim->set( 'username', 'trim_username' );
497 $trim->set( 'password', 'trim_password' );
498
499 $slinky = new Slinky( $url, $trim );
500 echo $slinky->short();
501
502 You could also do this if the URL was already a tr.im URL and you 
503 were going to make it longer, since Tr.im is supported for auto-detection:
504
505 $slinky = new Slinky( $url );
506 $slinky->set_service_from_url();
507 echo $slinky->long();
508
509 */
510 class Slinky_Trim extends Slinky_Service {
511         function url_is_short( $url ) {
512                 return stristr( $url, 'tr.im/' );
513         }
514         
515         function url_is_long( $url ) {
516                 return !stristr( $url, 'tr.im/' );
517         }
518         
519         function make_short( $url ) {
520                 $url = 'http://api.tr.im/api/trim_simple?url=' . urlencode( $url );
521
522                 if ( $this->get( 'username' ) && $this->get( 'password' ) )
523                         $url .= '&username=' . urlencode( $this->get( 'username' ) ) . '&password=' . urlencode( $this->get( 'password' ) );
524                 
525                 return $this->url_get( $url );
526         }
527         
528         function make_long( $url ) {
529                 $bits = parse_url( $url );
530                 $result = $this->url_get( 'http://api.tr.im/api/trim_destination.json?trimpath=' . substr( $bits['path'], 1 ) );
531                 $result = json_decode($result);
532                 if ( 'OK' == $result->status->result )
533                         return $result->destination;
534                 else
535                         return $url;
536         }
537 }
538
539 // Implementation of Is.Gd as a Slinky Service
540 class Slinky_IsGd extends Slinky_Service {
541         function url_is_short( $url ) {
542                 return stristr( $url, 'is.gd/' );
543         }
544         
545         function url_is_long( $url ) {
546                 return !stristr( $url, 'is.gd/' );
547         }
548         
549         function make_short( $url ) {
550                 $response = $this->url_get( 'http://is.gd/api.php?longurl=' . urlencode( $url ) );
551                 if ( 'error' == substr( strtolower( $response ), 0, 5 ) )
552                         return false;
553                 else
554                         return $response;
555         }
556 }
557
558 // Fon.gs
559 class Slinky_Fongs extends Slinky_Service {
560         function url_is_short( $url ) {
561                 return stristr( $url, 'fon.gs/' );
562         }
563         
564         function url_is_long( $url ) {
565                 return !stristr( $url, 'fon.gs/' );
566         }
567         
568         function make_short( $url ) {
569                 $response = $this->url_get( 'http://fon.gs/create.php?url=' . urlencode( $url ) );
570                 if ( 'OK:' == substr( $response, 0, 3 ) )
571                         return str_replace( 'OK: ', '', $response );
572                 else
573                         return $url;
574         }
575 }
576
577 // Micu.rl
578 class Slinky_Micurl extends Slinky_Service {
579         function url_is_short( $url ) {
580                 return stristr( $url, 'micurl.com/' );
581         }
582         
583         function url_is_long( $url ) {
584                 return !stristr( $url, 'micurl.com/' );
585         }
586         
587         function make_short( $url ) {
588                 $result = $this->url_get( 'http://micurl.com/api.php?url=' . urlencode( $url ) );
589                 if ( 1 != $result && 2 != $result )
590                         return 'http://micurl.com/' . $result;
591                 else
592                         return $url;
593         }
594 }
595
596 // ur1.ca
597 class Slinky_Ur1ca extends Slinky_Service {
598         function url_is_short( $url ) {
599                 return stristr( $url, 'ur1.ca/' );
600         }
601         
602         function url_is_long( $url ) {
603                 return !stristr( $url, 'ur1.ca/' );
604         }
605         
606         function make_short( $url ) {
607                 $result = $this->url_post( 'http://ur1.ca/', array( 'longurl' => $url ) );
608                 if ( preg_match( '/<p class="success">Your ur1 is: <a href="([^"]+)">/', $result, $matches ) )
609                         return $matches[1];
610                 else
611                         return $url;
612         }
613 }
614
615 // PtitURL.com
616 class Slinky_PtitURL extends Slinky_Service {
617         function url_is_short( $url ) {
618                 return stristr( $url, 'ptiturl.com/' );
619         }
620         
621         function url_is_long( $url ) {
622                 return !stristr( $url, 'ptiturl.com/' );
623         }
624         
625         function make_short( $url ) {
626                 $result = $this->url_get( 'http://ptiturl.com/index.php?creer=oui&url=' . urlencode( $url ) );
627                 if ( preg_match( '/<pre><a href=\'?([^\'>]+)\'?>/', $result, $matches ) )
628                         return $matches[1];
629                 else
630                         return $url;
631         }
632 }
633
634 // Tighturl.com
635 class Slinky_TightURL extends Slinky_Service {
636         function url_is_short( $url ) {
637                 return stristr( $url, 'tighturl.com/' )
638                                 || stristr( $url, '2tu.us/' );
639         }
640         
641         function url_is_long( $url ) {
642                 return !stristr( $url, 'tighturl.com/' )
643                                 && !stristr( $url, '2tu.us/' );
644         }
645         
646         function make_short( $url ) {
647                 $response = $this->url_get( 'http://tighturl.com/?save=y&url=' . urlencode( $url ) );
648                 if ( preg_match( '/Your tight URL is: <code><a href=\'([^\']+)\' target=\'_blank\'>/', $response, $matches ) ) {
649                         return $matches[1];
650                 } else {
651                         return $url;
652                 }
653         }
654 }
655
656 // Snipr for Slinky
657 /*
658 To use Snipr, you MUST set your user_id and API (key) for the service first, e.g.
659
660 $snipr = new Slinky_Snipr();
661 $snipr->set( 'user_id', 'Snipr User ID' );
662 $snipr->set( 'API', 'Snipr API Key' );
663
664 $slinky = new Slinky( $url, $snipr );
665 echo $slinky->short();
666
667 NOTE: Snipr requires the SimpleXML extension to be installed for lengthening URLs
668 */
669 class Slinky_Snipr extends Slinky_Service {
670         // Snipurl, Snurl, Snipr, Sn.im
671         function url_is_short( $url ) {
672                 return stristr( $url, 'snipr.com/' ) || stristr( $url, 'snipurl.com/' ) || stristr( $url, 'snurl.com/' ) || stristr( $url, 'sn.im/' );
673         }
674         
675         function url_is_long( $url ) {
676                 return !stristr( $url, 'snipr.com/' ) || !stristr( $url, 'snipurl.com/' ) || !stristr( $url, 'snurl.com/' ) || !stristr( $url, 'sn.im/' );
677         }
678         
679         function make_short( $url ) {
680                 if ( !$this->get( 'user_id' ) || !$this->get( 'API' ) )
681                         return $url;
682                 
683                 $response = $this->url_post( 'http://snipr.com/site/getsnip', array( 'sniplink' => urlencode( $url ), 'snipuser' => $this->get( 'user_id'), 'snipapi' => $this->get( 'API' ), 'snipformat' => 'simple' ) );
684                 if ( 'ERROR' != substr( $response, 0, 5 ) )
685                         return $response;
686                 else
687                         return $url;
688         }
689 }
690
691 // If you're testing things out, http://dentedreality.com.au/ should convert to:
692 // - http://tinyurl.com/jw5sh
693 // - http://bit.ly/hEkAD
694 // - http://tr.im/sk1H
695 // - http://is.gd/1yJ81
696 // - http://fon.gs/tc1p8c
697 // - http://micurl.com/qen3uub
698 // - http://ur1.ca/7dcd
699 // - http://ptiturl.com/?id=bac8fb
700 // - http://tighturl.com/kgd
701 // - http://snipr.com/nbbw3
702 // 
703 // $slinky = new Slinky( 'http://dentedreality.com.au/' );
704 // echo $slinky->short();