PageRenderTime 169ms CodeModel.GetById 80ms app.highlight 45ms RepoModel.GetById 36ms app.codeStats 0ms

/libraries/koowa/request/request.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 712 lines | 402 code | 100 blank | 210 comment | 81 complexity | f80fe5079b35ce06f8864d54f526ca60 MD5 | raw file
  1<?php
  2/**
  3 * @version    	$Id: request.php 4477 2012-02-10 01:06:38Z johanjanssens $
  4 * @category	Koowa
  5 * @package    	Koowa_Request
  6 * @copyright  	Copyright (C) 2007 - 2012 Johan Janssens. All rights reserved.
  7 * @license    	GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
  8 * @link 		http://www.nooku.org
  9 */
 10
 11//Instantiate the request singleton
 12KRequest::getInstance();
 13
 14/**
 15 * Request class
 16 *
 17 * @author      Johan Janssens <johan@nooku.org>
 18 * @category    Koowa
 19 * @package     Koowa_Request
 20 * @uses        KFilter
 21 * @uses        KInflector
 22 * @uses        KService
 23 * @static
 24 */
 25class KRequest
 26{
 27    /**
 28     * URL of the request regardless of the server
 29     *
 30     * @var KHttpUrl
 31     */
 32    protected static $_url = null;
 33
 34    /**
 35     * Base path of the request.
 36     *
 37     * @var KHttpUrl
 38     */
 39    protected static $_base = null;
 40
 41    /**
 42     * Root path of the request.
 43     *
 44     * @var KHttpUrl
 45     */
 46    protected static $_root = null;
 47
 48    /**
 49     * Referrer of the request
 50     *
 51     * @var KHttpUrl
 52     */
 53    protected static $_referrer = null;
 54
 55    /**
 56     * The raw post or put content information
 57     *
 58     * @var array
 59     */
 60    protected static $_content = null;
 61
 62    /**
 63     * The request accepts information
 64     *
 65     * @var array
 66     */
 67    protected static $_accept = null;
 68
 69
 70    /**
 71     * Constructor
 72     *
 73     * Prevent creating instances of this class by making the contructor private
 74     */
 75    final private function __construct(KConfig $config)
 76    {
 77        $content = self::content();
 78
 79        if(self::type() == 'HTTP')
 80        {
 81            if(strpos(PHP_SAPI, 'cgi') !== false) {
 82                $authorization = KRequest::get('server.REDIRECT_HTTP_AUTHORIZATION', 'string');
 83            } else {
 84                $authorization = KRequest::get('server.HTTP_AUTHORIZATION', 'url');
 85            }
 86            
 87	        if (strstr($authorization,"Basic"))
 88	        {
 89	            $parts = explode(':',base64_decode(substr($authorization, 6)));
 90
 91	            if (count($parts) == 2)
 92			    {
 93				    KRequest::set('server.PHP_AUTH_USER', $parts[0]);
 94				    KRequest::set('server.PHP_AUTH_PW'  , $parts[1]);
 95			    }
 96		    }
 97        }
 98
 99        if(!empty($content['data']))
100        {
101            if($content['type'] == 'application/x-www-form-urlencoded')
102            {
103                if (in_array(self::method(), array('PUT', 'DELETE')))
104                {
105                    parse_str($content['data'], $GLOBALS['_'.self::method()]);
106                    $GLOBALS['_REQUEST'] = array_merge($GLOBALS['_REQUEST'],  $GLOBALS['_'.self::method()]);
107                }
108            }
109
110            if($content['type'] == 'application/json')
111            {
112                if(in_array(self::method(), array('POST', 'PUT', 'DELETE')))
113                {
114                    $GLOBALS['_'.self::method()] = json_decode($content['data'], true);
115                    $GLOBALS['_REQUEST'] = array_merge($GLOBALS['_REQUEST'],  $GLOBALS['_'.self::method()]);
116                }
117            }
118        }
119     }
120
121    /**
122     * Clone
123     *
124     * Prevent creating clones of this class
125     */
126    final private function __clone() { }
127
128    /**
129     * Force creation of a singleton
130     *
131     * @return void
132     */
133    public static function getInstance($config = array())
134    {
135        static $instance;
136
137        if ($instance === NULL)
138        {
139            if(!$config instanceof KConfig) {
140                $config = new KConfig($config);
141            }
142
143            $instance = new self($config);
144        }
145
146        return $instance;
147    }
148
149
150    /**
151     * Get sanitized data from the request.
152     *
153     * @param   string              Variable identifier, prefixed by hash name eg post.foo.bar
154     * @param   mixed               Filter(s), can be a KFilter object, a filter name, an array of filter names or a filter identifier
155     * @param   mixed               Default value when the variable doesn't exist
156     * @throws  KRequestException   When an invalid filter was passed
157     * @return  mixed               The sanitized data
158     */
159    public static function get($identifier, $filter, $default = null)
160    {
161        list($hash, $keys) = self::_parseIdentifier($identifier);
162
163        $result = null;
164        if(isset($GLOBALS['_'.$hash]))
165        {
166            $result = $GLOBALS['_'.$hash];
167            foreach($keys as $key)
168            {
169                if(array_key_exists($key, $result)) {
170                    $result = $result[$key];
171                } else {
172                    $result = null;
173                    break;
174                }
175            }
176        }
177
178
179        // If the value is null return the default
180        if(is_null($result)) {
181            return $default;
182        }
183
184        // Handle magic quotes compatability
185        if (get_magic_quotes_gpc() && !in_array($hash, array('FILES', 'SESSION'))) {
186            $result = self::_stripSlashes( $result );
187        }
188
189        if(!($filter instanceof KFilterInterface)) {
190            $filter = KService::get('koowa:filter.factory')->instantiate($filter);
191        }
192
193        return $filter->sanitize($result);
194    }
195
196    /**
197     * Set a variable in the request. Cookies and session data are stored persistently.
198     *
199     * @param   mixed   Variable identifier, prefixed by hash name eg post.foo.bar
200     * @param   mixed   Variable value
201     */
202    public static function set($identifier, $value)
203    {
204        list($hash, $keys) = self::_parseIdentifier($identifier);
205        
206        // Add to _REQUEST hash if original hash is get, post, or cookies
207        if(in_array($hash, array('GET', 'POST', 'COOKIE'))) {
208            self::set('request.'.implode('.', $keys), $value);
209        }
210        
211        // Store cookies persistently
212        if($hash == 'COOKIE' && strpos(KRequest::protocol(), 'http') !== false)
213        {
214            // rewrite the $keys as foo[bar][bar]
215            $ckeys = $keys; // get a copy
216            $name = array_shift($ckeys);
217            foreach($ckeys as $ckey) {
218                $name .= '['.$ckey.']';
219            }
220 
221            if(!setcookie($name, $value)) {
222                throw new KRequestException("Couldn't set cookie, headers already sent.");
223            }
224        }
225
226        // Store in $GLOBALS
227        foreach(array_reverse($keys, true) as $key) {
228            $value = array($key => $value);
229        }
230        
231        // Add the global if it's doesn't exist
232        if(!isset($GLOBALS['_'.$hash])) { 
233           $GLOBALS['_'.$hash] = array(); 
234        } 
235        
236        $GLOBALS['_'.$hash] = KHelperArray::merge($GLOBALS['_'.$hash], $value);
237    }
238
239    /**
240     * Check if a variable exists based on an identifier
241     *
242     * @param   string  Variable identifier, prefixed by hash name eg post.foo.bar
243     * @return  boolean
244     */
245    public static function has($identifier)
246    {
247        list($hash, $keys) = self::_parseIdentifier($identifier);
248
249        foreach($keys as $key)
250        {
251            if(isset($GLOBALS['_'.$hash]) && array_key_exists($key, $GLOBALS['_'.$hash])) {
252                return true;
253            }
254        }
255
256        return false;
257    }
258
259    /**
260     * Get the POST or PUT raw content information
261     *
262     * The raw post data is not available with enctype="multipart/form-data".
263     *
264     * @param   string  The content data to return. Can be 'type' or 'data'.
265     *                  If not set, all the data will be returned.
266     * @return  array   An associative array with the content data. Valid keys are
267     *                  'type' and 'data'
268     */
269    public static function content($key = null)
270    {
271        $result = '';
272
273        if (!isset(self::$_content) && isset($_SERVER['CONTENT_TYPE']))
274        {
275            $type = $_SERVER['CONTENT_TYPE'];
276
277            // strip parameters from content-type like "; charset=UTF-8"
278            if (is_string($type))
279            {
280                if (preg_match('/^([^,\;]*)/', $type, $matches)) {
281                    $type = $matches[1];
282                }
283            }
284
285            self::$_content['type'] = $type;
286
287
288            $data = '';
289            if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0)
290            {
291                $input = fopen('php://input', 'r');
292                while ($chunk = fread($input, 1024)) {
293                    $data .= $chunk;
294                }
295
296                fclose($input);
297            }
298
299            self::$_content['data'] = $data;
300        }
301
302        return isset($key) ? self::$_content[$key] : self::$_content;
303    }
304
305    /**
306     * Get the accept request information
307     *
308     * @param   string  The accept data to return. Can be 'format', 'encoding' or 'language'.
309     *                  If not set, all the accept data will be returned.
310     * @return  array   An associative array with the content data. Valid keys are
311     *                  'format', 'encoding' and 'language'
312     */
313    public static function accept($type = null)
314    {
315        if (!isset(self::$_accept) && isset($_SERVER['HTTP_ACCEPT']))
316        {
317            $accept = KRequest::get('server.HTTP_ACCEPT', 'string');
318            self::$_accept['format'] = self::_parseAccept($accept);
319
320            if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
321            {
322                $accept = KRequest::get('server.HTTP_ACCEPT_ENCODING', 'string');
323                self::$_accept['encoding'] = self::_parseAccept($accept);
324            }
325
326            if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
327            {
328                $accept = KRequest::get('server.HTTP_ACCEPT_LANGUAGE', 'string');
329                self::$_accept['language'] = self::_parseAccept($accept);
330            }
331        }
332
333        return $type ? self::$_accept[$type] : self::$_accept;
334    }
335
336    /**
337     * Returns the client information doing the request
338     *
339     * @return string $_SERVER['HTTP_USER_AGENT'] or an empty string if it's not supplied in the request
340     */
341    public static function client()
342    {
343        return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
344    }
345
346    /**
347     * Returns the HTTP referrer.
348     *
349     * 'referer' a commonly used misspelling word for 'referrer'
350     * @see     http://en.wikipedia.org/wiki/HTTP_referrer
351     *
352     * @param   boolean     Only allow internal url's
353     * @return  KHttpUrl    A KHttpUrl object
354     */
355    public static function referrer($isInternal = true)
356    {
357        if(!isset(self::$_referrer))
358        {
359            if($referrer = KRequest::get('server.HTTP_REFERER', 'url'))
360            {
361                self::$_referrer = KService::get('koowa:http.url', array('url' => $referrer));
362
363                if($isInternal)
364                {
365                    if(!KService::get('koowa:filter.internalurl')->validate((string)self::$_referrer)) {
366                        return null;
367                    }
368                }
369            }
370        }
371
372        return self::$_referrer;
373    }
374
375    /**
376     * Return the URI of the request regardless of the server
377     *
378     * @return  KHttpUrl    A KHttpUri object
379     */
380    public static function url()
381    {
382        if(!isset(self::$_url))
383        {
384            $url = self::protocol().'://';
385            
386            if (PHP_SAPI !== 'cli') 
387        	{
388        		/*
389            	 * Since we are assigning the URI from the server variables, we first need
390             	 * to determine if we are running on apache or IIS.  If PHP_SELF and REQUEST_URI
391             	 * are present, we will assume we are running on apache.
392             	 */
393        	    if (!empty ($_SERVER['PHP_SELF']) && !empty ($_SERVER['REQUEST_URI']))
394                {
395                	/*
396                 	 * To build the entire URI we need to prepend the protocol, and the http host
397                 	 * to the URI string.
398                 	 */
399                    $url .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
400
401                	/*
402                 	 * Since we do not have REQUEST_URI to work with, we will assume we are
403                 	 * running on IIS and will therefore need to work some magic with the SCRIPT_NAME and
404                 	 * QUERY_STRING environment variables.
405                 	 */
406                }
407                else
408                {
409                    // IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable
410                    $url .= $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'];
411
412                    // If the query string exists append it to the URI string
413                    if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
414                        $url .= '?' . $_SERVER['QUERY_STRING'];
415                    }
416                }
417        	}
418        	else $url .= 'koowa';
419            
420            // Sanitize the url since we can't trust the server var
421            $url = KService::get('koowa:filter.url')->sanitize($url);
422
423            // Create the URI object
424            self::$_url = KService::get('koowa:http.url', array('url' => $url));
425
426        }
427
428        return self::$_url;
429    }
430
431    /**
432     * Returns the base path of the request.
433     *
434     * @return  object  A KHttpUrl object
435     */
436    public static function base()
437    {
438        if(!isset(self::$_base))
439        {
440            // Get the base request path
441            if (strpos(PHP_SAPI, 'cgi') !== false && !ini_get('cgi.fix_pathinfo')  && !empty($_SERVER['REQUEST_URI'])) 
442            {    
443                // PHP-CGI on Apache with "cgi.fix_pathinfo = 0"
444                // We don't have user-supplied PATH_INFO in PHP_SELF
445                $path = $_SERVER['PHP_SELF'];
446            } 
447            else $path = $_SERVER['SCRIPT_NAME'];
448            
449            $path = rtrim(dirname($path), '/\\');
450         
451            // Sanitize the url since we can't trust the server var
452            $path = KService::get('koowa:filter.url')->sanitize($path);
453
454            self::$_base = KService::get('koowa:http.url', array('url' => $path));
455        }
456
457        return self::$_base;
458    }
459
460    /**
461     * Returns the root path of the request.
462     *
463     * In most case this value will be the same as KRequest::base however it can be
464     * changed by pushing in a different value
465     *
466     * @return  object  A KHttpUrl object
467     */
468    public static function root($path = null)
469    {
470        if(!is_null($path))
471        {
472            if(!$path instanceof KhttpUrl) {
473                $path = KService::get('koowa:http.url', array('url' => $path));
474            }
475
476            self::$_root = $path;
477        }
478
479        if(is_null(self::$_root)) {
480            self::$_root = self::$_base;
481        }
482
483        return self::$_root;
484    }
485
486    /**
487     * Returns the current request protocol, based on $_SERVER['https']. In CLI
488     * mode, 'cli' will be returned.
489     *
490     * @return  string
491     */
492    public static function protocol()
493    {
494        $protocol = 'cli';
495        
496        if (PHP_SAPI !== 'cli') 
497        {
498            $protocol = 'http';
499            
500            if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {
501                $protocol = 'https';
502            }
503        } 
504     
505        return $protocol;
506    }
507
508    /**
509     * Returns current request method.
510     *
511     * @return  string
512     */
513    public static function method()
514    {
515        $method = '';
516
517        if(PHP_SAPI != 'cli')
518        {
519            $method  =  strtoupper($_SERVER['REQUEST_METHOD']);
520
521            if($method == 'POST')
522            {
523                if(isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
524                    $method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
525                }
526
527                if(self::has('post._method')) {
528                    $method = strtoupper(self::get('post._method', 'cmd'));
529                }
530            }
531        }
532
533        return $method;
534    }
535
536    /**
537     * Return the current request transport type.
538     *
539     * @return  string
540     */
541    public static function type()
542    {
543        $type = 'HTTP';
544
545        if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
546            $type = 'AJAX';
547        }
548
549        if( isset($_SERVER['HTTP_X_FLASH_VERSION'])) {
550            $type = 'FLASH';
551        }
552
553        if(preg_match('/^(Shockwave|Adobe) Flash/', KRequest::client()) == 1) {
554             $type = 'FLASH';
555        }
556
557        return $type;
558    }
559
560    /**
561     * Return the request token
562     *
563     * @return  string  The request token or NULL if no token could be found
564     */
565    public static function token()
566    {
567        $token = null;
568
569        if(self::has('server.HTTP_X_TOKEN')) {
570            $token = self::get('server.HTTP_X_TOKEN', 'md5');
571        }
572
573        if(self::has('request._token')) {
574            $token = self::get('request._token', 'md5');
575        }
576
577        return $token;
578    }
579
580    /**
581     * Return the request format
582     *
583     * This function tries to find the format by inspecting the accept header,
584     * only if one accept type is specified the format will be parsed from it,
585     * otherwise the path extension or the 'format' request variable is used.
586     *
587     * @return  string  The request format or NULL if no format could be found
588     */
589    public static function format()
590    {
591        $format = null;
592
593        if(count(self::accept('format')) == 1)
594        {
595            $mime   = explode('/', key(self::accept('format')));
596            $format = $mime[1];
597
598            if($pos = strpos($format, '+')) {
599                $format = substr($format, 0, $pos);
600            }
601
602            //Format cannot be *
603            if($format == '*') {
604                $format = null;
605            }
606        }
607
608        if(self::has('request.format')) {
609            $format = self::get('request.format', 'word');
610        }
611
612        return $format;
613    }
614
615    /**
616     * Parse the variable identifier
617     *
618     * @param   string  Variable identifier
619     * @return  array   0 => hash, 1 => parts
620     */
621    protected static function _parseIdentifier($identifier)
622    {
623        $parts = array();
624        $hash  = $identifier;
625
626        // Validate the variable format
627        if(strpos($identifier, '.') !== false)
628        {
629            // Split the variable name into it's parts
630            $parts = explode('.', $identifier);
631
632            // Validate the hash name
633            $hash   = array_shift($parts);
634        }
635
636        $hash = strtoupper($hash);
637
638        return array($hash, $parts);
639    }
640
641    /**
642     * Parses an accept header and returns an array (type => quality) of the
643     * accepted types, ordered by quality.
644     *
645     * @param string    header to parse
646     * @param array     default values
647     * @return array
648     */
649    protected static function _parseAccept( $accept, array $defaults = NULL)
650    {
651        if (!empty($accept))
652        {
653            // Get all of the types
654            $types = explode(',', $accept);
655
656            foreach ($types as $type)
657            {
658                // Split the type into parts
659                $parts = explode(';', $type);
660
661                // Make the type only the MIME
662                $type = trim(array_shift($parts));
663
664                // Default quality is 1.0
665                $quality = 1.0;
666
667                foreach ($parts as $part)
668                {
669                    // Prevent undefined $value notice below
670                    if (strpos($part, '=') === FALSE) {
671                        continue;
672                    }
673
674                    // Separate the key and value
675                    list ($key, $value) = explode('=', trim($part));
676
677                    if ($key === 'q')
678                    {
679                        // There is a quality for this type
680                        $quality = (float) trim($value);
681                    }
682                }
683
684                // Add the accept type and quality
685                $defaults[$type] = $quality;
686            }
687        }
688
689        // Make sure that accepts is an array
690        $accepts = (array) $defaults;
691
692        // Order by quality
693        arsort($accepts);
694
695        return $accepts;
696    }
697
698    /**
699     * Strips slashes recursively on an array
700     *
701     * @param   array   Array of (nested arrays of) strings
702     * @return  array   The input array with stripshlashes applied to it
703     */
704    protected static function _stripSlashes( $value )
705    {
706        if(!is_object($value)) {
707            $value = is_array( $value ) ? array_map( array( 'KRequest', '_stripSlashes' ), $value ) : stripslashes( $value );
708        }
709
710        return $value;
711    }
712}