1+ <?php
2+ /**
3+ * Created by PhpStorm.
4+ * User: inhere
5+ * Date: 2017-10-20
6+ * Time: 13:20
7+ */
8+
9+ namespace Inhere \Http ;
10+
11+ use Inhere \Library \Collections \CollectionInterface ;
12+ use Inhere \Library \Collections \SimpleCollection ;
13+ use Psr \Http \Message \RequestInterface ;
14+ use Psr \Http \Message \ResponseInterface ;
15+ use Psr \Http \Message \ServerRequestInterface ;
16+ use Psr \Http \Message \StreamInterface ;
17+ use Psr \Http \Message \UploadedFileInterface ;
18+ use Psr \Http \Message \UriInterface ;
19+
20+ /**
21+ * Class HttpFactory
22+ * @package Inhere\Http
23+ * @link https://github.com/php-fig/fig-standards/blob/master/proposed/http-factory/http-factory.md
24+ */
25+ class HttpFactory
26+ {
27+ /**
28+ * Special HTTP headers that do not have the "HTTP_" prefix
29+ * @var array
30+ */
31+ protected static $ special = [
32+ 'CONTENT_TYPE ' => 1 ,
33+ 'CONTENT_LENGTH ' => 1 ,
34+ 'PHP_AUTH_USER ' => 1 ,
35+ 'PHP_AUTH_PW ' => 1 ,
36+ 'PHP_AUTH_DIGEST ' => 1 ,
37+ 'AUTH_TYPE ' => 1 ,
38+ ];
39+
40+ /**
41+ * RequestFactoryInterface
42+ */
43+
44+ /**
45+ * Create a new request.
46+ * @param string $method
47+ * @param UriInterface|string $uri
48+ * @return RequestInterface
49+ */
50+ public static function createRequest ($ method , $ uri )
51+ {
52+ return new Request ($ method , $ uri );
53+ }
54+
55+ /**
56+ * ResponseFactoryInterface
57+ */
58+
59+ /**
60+ * Create a new response.
61+ * @param integer $code HTTP status code
62+ * @return ResponseInterface
63+ */
64+ public static function createResponse ($ code = 200 )
65+ {
66+ return new Response ($ code );
67+ }
68+
69+ /**
70+ * ServerRequestFactoryInterface
71+ */
72+
73+ /**
74+ * Create a new server request.
75+ * @param string $method
76+ * @param UriInterface|string $uri
77+ * @return ServerRequestInterface
78+ */
79+ public static function createServerRequest ($ method , $ uri )
80+ {
81+ return new Request ($ method , $ uri );
82+ }
83+
84+ /**
85+ * Create a new server request from server variables.
86+ * @param array $server Typically $_SERVER or similar structure.
87+ * @return ServerRequestInterface
88+ * @throws \InvalidArgumentException
89+ * If no valid method or URI can be determined.
90+ */
91+ public static function createServerRequestFromArray (array $ server )
92+ {
93+ $ env = new SimpleCollection ($ server );
94+ $ method = $ env ['REQUEST_METHOD ' ];
95+ $ uri = static ::createUriFromArray ($ env );
96+ $ headers = static ::createHeadersFromArray ($ env );
97+ $ cookies = Cookies::parseFromRawHeader ($ headers ->get ('Cookie ' , []));
98+ $ serverParams = $ env ->all ();
99+ $ body = new RequestBody ();
100+ $ uploadedFiles = UploadedFile::createFromFILES ();
101+
102+ $ request = new Request ($ method , $ uri , $ headers , $ cookies , $ serverParams , $ body , $ uploadedFiles );
103+
104+ if ($ method === 'POST ' &&
105+ in_array ($ request ->getMediaType (), ['application/x-www-form-urlencoded ' , 'multipart/form-data ' ], true )
106+ ) {
107+ // parsed body must be $_POST
108+ $ request = $ request ->withParsedBody ($ _POST );
109+ }
110+
111+ return $ request ;
112+ }
113+
114+ /**
115+ * StreamFactoryInterface
116+ */
117+
118+ /**
119+ * Create a new stream from a string.
120+ * The stream SHOULD be created with a temporary resource.
121+ * @param string $content
122+ * @return StreamInterface
123+ */
124+ public static function createStream ($ content = '' )
125+ {
126+ return new RequestBody ($ content );
127+ }
128+
129+ /**
130+ * Create a stream from an existing file.
131+ * The file MUST be opened using the given mode, which may be any mode
132+ * supported by the `fopen` function.
133+ * The `$filename` MAY be any string supported by `fopen()`.
134+ * @param string $filename
135+ * @param string $mode
136+ * @return StreamInterface
137+ */
138+ public static function createStreamFromFile ($ filename , $ mode = 'r ' )
139+ {
140+ // $stream = fopen('php://temp', $mode);
141+ $ stream = fopen ($ filename , $ mode );
142+
143+ return new Body ($ stream );
144+ }
145+
146+ /**
147+ * Create a new stream from an existing resource.
148+ * The stream MUST be readable and may be writable.
149+ * @param resource $resource e.g `$resource = fopen('php://temp', 'r+');`
150+ * @return StreamInterface
151+ */
152+ public static function createStreamFromResource ($ resource )
153+ {
154+ return new Body ($ resource );
155+ }
156+
157+ /**
158+ * UploadedFileFactoryInterface
159+ */
160+
161+ /**
162+ * Create a new uploaded file.
163+ * If a string is used to create the file, a temporary resource will be
164+ * created with the content of the string.
165+ * If a size is not provided it will be determined by checking the size of
166+ * the file.
167+ * @see http://php.net/manual/features.file-upload.post-method.php
168+ * @see http://php.net/manual/features.file-upload.errors.php
169+ * @param string|resource $file
170+ * @param integer $size in bytes
171+ * @param integer $error PHP file upload error
172+ * @param string $clientFilename
173+ * @param string $clientMediaType
174+ * @return UploadedFileInterface
175+ * @throws \InvalidArgumentException If the file resource is not readable.
176+ */
177+ public static function createUploadedFile (
178+ $ file , $ size = null , $ error = \UPLOAD_ERR_OK ,
179+ $ clientFilename = null , $ clientMediaType = null
180+ )
181+ {
182+ return new UploadedFile ($ file , $ clientFilename , $ clientMediaType , $ size , $ error );
183+ }
184+
185+ /**
186+ * UriFactoryInterface
187+ */
188+
189+ /**
190+ * Create a new URI.
191+ * @param string $uri
192+ * @return UriInterface
193+ * @throws \InvalidArgumentException If the given URI cannot be parsed.
194+ */
195+ public function createUri ($ uri = '' )
196+ {
197+ return Uri::createFromString ($ uri );
198+ }
199+
200+ /*******************************************************************************
201+ * extended factory methods
202+ ******************************************************************************/
203+
204+ /**
205+ * @param CollectionInterface|array $env
206+ * @return Headers
207+ */
208+ public static function createHeadersFromArray ($ env )
209+ {
210+ $ data = [];
211+ $ env = self ::ensureIsCollection ($ env );
212+ $ env = self ::determineAuthorization ($ env );
213+
214+ foreach ($ env as $ key => $ value ) {
215+ $ key = strtoupper ($ key );
216+ if (isset (static ::$ special [$ key ]) || strpos ($ key , 'HTTP_ ' ) === 0 ) {
217+ if ($ key !== 'HTTP_CONTENT_LENGTH ' ) {
218+ $ data [$ key ] = $ value ;
219+ }
220+ }
221+ }
222+
223+ return new Headers ($ data );
224+ }
225+
226+ /**
227+ * If HTTP_AUTHORIZATION does not exist tries to get it from
228+ * getallheaders() when available.
229+ * @param CollectionInterface $env The Slim application SimpleCollection
230+ * @return CollectionInterface
231+ */
232+ public static function determineAuthorization ($ env )
233+ {
234+ $ authorization = $ env ->get ('HTTP_AUTHORIZATION ' );
235+
236+ if (null === $ authorization && is_callable ('getallheaders ' )) {
237+ $ headers = getallheaders ();
238+ $ headers = array_change_key_case ($ headers , CASE_LOWER );
239+ if (isset ($ headers ['authorization ' ])) {
240+ $ env ->set ('HTTP_AUTHORIZATION ' , $ headers ['authorization ' ]);
241+ }
242+ }
243+
244+ return $ env ;
245+ }
246+
247+ /**
248+ * @param CollectionInterface|array $env
249+ * @return Uri
250+ */
251+ public static function createUriFromArray ($ env )
252+ {
253+ $ env = self ::ensureIsCollection ($ env );
254+
255+ // Scheme
256+ $ isSecure = $ env ->get ('HTTPS ' );
257+ $ scheme = (empty ($ isSecure ) || $ isSecure === 'off ' ) ? 'http ' : 'https ' ;
258+
259+ // Authority: Username and password
260+ $ username = $ env ->get ('PHP_AUTH_USER ' , '' );
261+ $ password = $ env ->get ('PHP_AUTH_PW ' , '' );
262+
263+ // Authority: Host
264+ if ($ env ->has ('HTTP_HOST ' )) {
265+ $ host = $ env ->get ('HTTP_HOST ' );
266+ } else {
267+ $ host = $ env ->get ('SERVER_NAME ' );
268+ }
269+
270+ // Authority: Port
271+ $ port = (int )$ env ->get ('SERVER_PORT ' , 80 );
272+ if (preg_match ('/^(\[[a-fA-F0-9:.]+\])(:\d+)?\z/ ' , $ host , $ matches )) {
273+ $ host = $ matches [1 ];
274+
275+ if ($ matches [2 ]) {
276+ $ port = (int )substr ($ matches [2 ], 1 );
277+ }
278+ } else {
279+ $ pos = strpos ($ host , ': ' );
280+ if ($ pos !== false ) {
281+ $ port = (int )substr ($ host , $ pos + 1 );
282+ $ host = strstr ($ host , ': ' , true );
283+ }
284+ }
285+
286+ // Path
287+ $ requestScriptName = parse_url ($ env ->get ('SCRIPT_NAME ' ), PHP_URL_PATH );
288+ $ requestScriptDir = dirname ($ requestScriptName );
289+
290+ // parse_url() requires a full URL. As we don't extract the domain name or scheme,
291+ // we use a stand-in.
292+ $ requestUri = parse_url ('http://example.com ' . $ env ->get ('REQUEST_URI ' ), PHP_URL_PATH );
293+
294+ $ basePath = '' ;
295+ $ virtualPath = $ requestUri ;
296+ if (stripos ($ requestUri , $ requestScriptName ) === 0 ) {
297+ $ basePath = $ requestScriptName ;
298+ } elseif ($ requestScriptDir !== '/ ' && stripos ($ requestUri , $ requestScriptDir ) === 0 ) {
299+ $ basePath = $ requestScriptDir ;
300+ }
301+
302+ if ($ basePath ) {
303+ $ virtualPath = ltrim (substr ($ requestUri , strlen ($ basePath )), '/ ' );
304+ }
305+
306+ // Query string
307+ $ queryString = $ env ->get ('QUERY_STRING ' , '' );
308+ if ($ queryString === '' ) {
309+ $ queryString = parse_url ('http://example.com ' . $ env ->get ('REQUEST_URI ' ), PHP_URL_QUERY );
310+ }
311+
312+ // Fragment
313+ $ fragment = '' ;
314+
315+ // Build Uri
316+ $ uri = new Uri ($ scheme , $ host , $ port , $ virtualPath , $ queryString , $ fragment , $ username , $ password );
317+ if ($ basePath ) {
318+ $ uri = $ uri ->withBasePath ($ basePath );
319+ }
320+
321+ return $ uri ;
322+ }
323+
324+ /**
325+ * @param $data
326+ * @return CollectionInterface
327+ */
328+ public static function ensureIsCollection ($ data )
329+ {
330+ if (is_array ($ data )) {
331+ return new SimpleCollection ($ data );
332+ }
333+
334+ if ($ data instanceof CollectionInterface) {
335+ return $ data ;
336+ }
337+
338+ return new SimpleCollection ((array )$ data );
339+ }
340+ }
0 commit comments