1: <?php
2: /**
3: * Slim - a micro PHP 5 framework
4: *
5: * @author Josh Lockhart <info@slimframework.com>
6: * @copyright 2011 Josh Lockhart
7: * @link http://www.slimframework.com
8: * @license http://www.slimframework.com/license
9: * @version 2.2.0
10: * @package Slim
11: *
12: * MIT LICENSE
13: *
14: * Permission is hereby granted, free of charge, to any person obtaining
15: * a copy of this software and associated documentation files (the
16: * "Software"), to deal in the Software without restriction, including
17: * without limitation the rights to use, copy, modify, merge, publish,
18: * distribute, sublicense, and/or sell copies of the Software, and to
19: * permit persons to whom the Software is furnished to do so, subject to
20: * the following conditions:
21: *
22: * The above copyright notice and this permission notice shall be
23: * included in all copies or substantial portions of the Software.
24: *
25: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28: * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29: * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30: * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31: * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32: */
33: namespace Slim;
34:
35: /**
36: * Environment
37: *
38: * This class creates and returns a key/value array of common
39: * environment variables for the current HTTP request.
40: *
41: * This is a singleton class; derived environment variables will
42: * be common across multiple Slim applications.
43: *
44: * This class matches the Rack (Ruby) specification as closely
45: * as possible. More information available below.
46: *
47: * @package Slim
48: * @author Josh Lockhart
49: * @since 1.6.0
50: */
51: class Environment implements \ArrayAccess, \IteratorAggregate
52: {
53: /**
54: * @var array
55: */
56: protected $properties;
57:
58: /**
59: * @var \Slim\Environment
60: */
61: protected static $environment;
62:
63: /**
64: * Get environment instance (singleton)
65: *
66: * This creates and/or returns an environment instance (singleton)
67: * derived from $_SERVER variables. You may override the global server
68: * variables by using `\Slim\Environment::mock()` instead.
69: *
70: * @param bool $refresh Refresh properties using global server variables?
71: * @return \Slim\Environment
72: */
73: public static function getInstance($refresh = false)
74: {
75: if (is_null(self::$environment) || $refresh) {
76: self::$environment = new self();
77: }
78:
79: return self::$environment;
80: }
81:
82: /**
83: * Get mock environment instance
84: *
85: * @param array $userSettings
86: * @return \Slim\Environment
87: */
88: public static function mock($userSettings = array())
89: {
90: self::$environment = new self(array_merge(array(
91: 'REQUEST_METHOD' => 'GET',
92: 'SCRIPT_NAME' => '',
93: 'PATH_INFO' => '',
94: 'QUERY_STRING' => '',
95: 'SERVER_NAME' => 'localhost',
96: 'SERVER_PORT' => 80,
97: 'ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
98: 'ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
99: 'ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
100: 'USER_AGENT' => 'Slim Framework',
101: 'REMOTE_ADDR' => '127.0.0.1',
102: 'slim.url_scheme' => 'http',
103: 'slim.input' => '',
104: 'slim.errors' => @fopen('php://stderr', 'w')
105: ), $userSettings));
106:
107: return self::$environment;
108: }
109:
110: /**
111: * Constructor (private access)
112: *
113: * @param array|null $settings If present, these are used instead of global server variables
114: */
115: private function __construct($settings = null)
116: {
117: if ($settings) {
118: $this->properties = $settings;
119: } else {
120: $env = array();
121:
122: //The HTTP request method
123: $env['REQUEST_METHOD'] = $_SERVER['REQUEST_METHOD'];
124:
125: //The IP
126: $env['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
127:
128: /**
129: * Application paths
130: *
131: * This derives two paths: SCRIPT_NAME and PATH_INFO. The SCRIPT_NAME
132: * is the real, physical path to the application, be it in the root
133: * directory or a subdirectory of the public document root. The PATH_INFO is the
134: * virtual path to the requested resource within the application context.
135: *
136: * With htaccess, the SCRIPT_NAME will be an absolute path (without file name);
137: * if not using htaccess, it will also include the file name. If it is "/",
138: * it is set to an empty string (since it cannot have a trailing slash).
139: *
140: * The PATH_INFO will be an absolute path with a leading slash; this will be
141: * used for application routing.
142: */
143: if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0) {
144: $env['SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME']; //Without URL rewrite
145: } else {
146: $env['SCRIPT_NAME'] = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']) ); //With URL rewrite
147: }
148: $env['PATH_INFO'] = substr_replace($_SERVER['REQUEST_URI'], '', 0, strlen($env['SCRIPT_NAME']));
149: if (strpos($env['PATH_INFO'], '?') !== false) {
150: $env['PATH_INFO'] = substr_replace($env['PATH_INFO'], '', strpos($env['PATH_INFO'], '?')); //query string is not removed automatically
151: }
152: $env['SCRIPT_NAME'] = rtrim($env['SCRIPT_NAME'], '/');
153: $env['PATH_INFO'] = '/' . ltrim($env['PATH_INFO'], '/');
154:
155: //The portion of the request URI following the '?'
156: $env['QUERY_STRING'] = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
157:
158: //Name of server host that is running the script
159: $env['SERVER_NAME'] = $_SERVER['SERVER_NAME'];
160:
161: //Number of server port that is running the script
162: $env['SERVER_PORT'] = $_SERVER['SERVER_PORT'];
163:
164: //HTTP request headers
165: $specialHeaders = array('CONTENT_TYPE', 'CONTENT_LENGTH', 'PHP_AUTH_USER', 'PHP_AUTH_PW', 'PHP_AUTH_DIGEST', 'AUTH_TYPE');
166: foreach ($_SERVER as $key => $value) {
167: $value = is_string($value) ? trim($value) : $value;
168: if (strpos($key, 'HTTP_') === 0) {
169: $env[substr($key, 5)] = $value;
170: } elseif (strpos($key, 'X_') === 0 || in_array($key, $specialHeaders)) {
171: $env[$key] = $value;
172: }
173: }
174:
175: //Is the application running under HTTPS or HTTP protocol?
176: $env['slim.url_scheme'] = empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ? 'http' : 'https';
177:
178: //Input stream (readable one time only; not available for mutipart/form-data requests)
179: $rawInput = @file_get_contents('php://input');
180: if (!$rawInput) {
181: $rawInput = '';
182: }
183: $env['slim.input'] = $rawInput;
184:
185: //Error stream
186: $env['slim.errors'] = fopen('php://stderr', 'w');
187:
188: $this->properties = $env;
189: }
190: }
191:
192: /**
193: * Array Access: Offset Exists
194: */
195: public function offsetExists($offset)
196: {
197: return isset($this->properties[$offset]);
198: }
199:
200: /**
201: * Array Access: Offset Get
202: */
203: public function offsetGet($offset)
204: {
205: if (isset($this->properties[$offset])) {
206: return $this->properties[$offset];
207: } else {
208: return null;
209: }
210: }
211:
212: /**
213: * Array Access: Offset Set
214: */
215: public function offsetSet($offset, $value)
216: {
217: $this->properties[$offset] = $value;
218: }
219:
220: /**
221: * Array Access: Offset Unset
222: */
223: public function offsetUnset($offset)
224: {
225: unset($this->properties[$offset]);
226: }
227:
228: /**
229: * IteratorAggregate
230: *
231: * @return \ArrayIterator
232: */
233: public function getIterator()
234: {
235: return new \ArrayIterator($this->properties);
236: }
237: }
238: