├── .gitignore
├── LICENSE
├── Proxy.php
├── README.md
├── composer.json
├── composer.lock
└── docker-compose.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 |
4 | vendor/
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/Proxy.php:
--------------------------------------------------------------------------------
1 |
11 | * @license http://unlicense.org
12 | * @package Zounar\PHPProxy
13 | *
14 | * Credits to:
15 | * https://github.com/cowboy/php-simple-proxy/
16 | * https://gist.github.com/iovar
17 | *
18 | * Usage:
19 | * To call this script two headers must be sent
20 | * HTTP_PROXY_AUTH Access key for the proxy (should be changed)
21 | * HTTP_PROXY_TARGET_URL URL to be called by this script
22 | *
23 | * Debug:
24 | * To debug, send HTTP_PROXY_DEBUG header with any non-zero value
25 | *
26 | * Compatibility:
27 | * PHP >=5.6
28 | * libcurl
29 | * gzip
30 | * PHP safe_mode disabled
31 | */
32 | class Proxy
33 | {
34 | /**
35 | * Your private auth key. It is recommended to change it.
36 | * If you installed the package via composer, call `Proxy::$AUTH_KEY = '';` before running the proxy.
37 | * If you copied this file, change the value here in place.
38 | * @var string
39 | */
40 | public static $AUTH_KEY = 'Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2';
41 |
42 | /**
43 | * Set this to false to disable authorization. Useful for debugging, not recommended in production.
44 | * @var bool
45 | */
46 | public static $ENABLE_AUTH = true;
47 |
48 | /**
49 | * If true, PHP safe mode compatibility will not be checked
50 | * (you may not need it if no POST files are sent over proxy)
51 | * @var bool
52 | */
53 | public static $IGNORE_SAFE_MODE = false;
54 |
55 | /**
56 | * Enable debug mode (you can do it by sending Proxy-Debug header as well).
57 | * This value overrides any value specified in Proxy-Debug header.
58 | * @var bool
59 | */
60 | public static $DEBUG = false;
61 |
62 | /**
63 | * When set to false the fetched header is not included in the result
64 | * @var bool
65 | */
66 | public static $CURLOPT_HEADER = true;
67 |
68 | /**
69 | * When set to false the fetched result is echoed immediately instead of waiting for the fetch to complete first
70 | * @var bool
71 | */
72 | public static $CURLOPT_RETURNTRANSFER = true;
73 |
74 | /**
75 | * Target URL is set via Proxy-Target-URL header. For debugging purposes you might set it directly here.
76 | * This value overrides any value specified in Proxy-Target-URL header.
77 | * @var string
78 | */
79 | public static $TARGET_URL = '';
80 |
81 | /**
82 | * Name of remote debug header
83 | * @var string
84 | */
85 | public static $HEADER_HTTP_PROXY_DEBUG = 'HTTP_PROXY_DEBUG';
86 |
87 | /**
88 | * Name of the proxy auth key header
89 | * @var string
90 | */
91 | public static $HEADER_HTTP_PROXY_AUTH = 'HTTP_PROXY_AUTH';
92 |
93 | /**
94 | * Name of the target url header
95 | * @var string
96 | */
97 | public static $HEADER_HTTP_PROXY_TARGET_URL = 'HTTP_PROXY_TARGET_URL';
98 |
99 | /**
100 | * Line break for debug purposes
101 | * @var string
102 | */
103 | protected static $HR = PHP_EOL . PHP_EOL . '----------------------------------------------' . PHP_EOL . PHP_EOL;
104 |
105 | /**
106 | * @return string[]
107 | */
108 | protected static function getSkippedHeaders()
109 | {
110 | return [
111 | static::$HEADER_HTTP_PROXY_TARGET_URL,
112 | static::$HEADER_HTTP_PROXY_AUTH,
113 | static::$HEADER_HTTP_PROXY_DEBUG,
114 | 'HTTP_HOST',
115 | 'HTTP_ACCEPT_ENCODING'
116 | ];
117 | }
118 |
119 | /**
120 | * Return variable or default value if not set
121 | * @param mixed $variable
122 | * @param mixed|null $default
123 | * @return mixed
124 | * @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection
125 | */
126 | protected static function ri(&$variable, $default = null)
127 | {
128 | if (isset($variable)) {
129 | return $variable;
130 | } else {
131 | return $default;
132 | }
133 | }
134 |
135 | /**
136 | * @param string $message
137 | */
138 | protected static function exitWithError($message)
139 | {
140 | http_response_code(500);
141 | echo 'PROXY ERROR: ' . $message;
142 | exit(500);
143 | }
144 |
145 | /**
146 | * @return bool
147 | */
148 | public static function isInstalledWithComposer()
149 | {
150 | $autoloaderPath = join(DIRECTORY_SEPARATOR, [dirname(dirname(__DIR__)), 'autoload.php']);
151 | return is_readable($autoloaderPath);
152 | }
153 |
154 | /**
155 | * @return void
156 | */
157 | public static function registerErrorHandlers()
158 | {
159 | set_error_handler(function ($code, $message, $file, $line) {
160 | Proxy::exitWithError("($code) $message in $file at line $line");
161 | }, E_ALL);
162 |
163 | set_exception_handler(function (Exception $ex) {
164 | Proxy::exitWithError("{$ex->getMessage()} in {$ex->getFile()} at line {$ex->getLine()}");
165 | });
166 | }
167 |
168 | /**
169 | * @return void
170 | */
171 | public static function checkCompatibility()
172 | {
173 | if (!static::$IGNORE_SAFE_MODE && function_exists('ini_get') && ini_get('safe_mode')) {
174 | throw new RuntimeException('Safe mode is enabled, this may cause problems with uploading files');
175 | }
176 |
177 | if (!function_exists('curl_init')) {
178 | throw new RuntimeException('libcurl is not installed on this server');
179 | }
180 |
181 | if (!function_exists('gzdecode')) {
182 | throw new RuntimeException('gzip is not installed on this server');
183 | }
184 | }
185 |
186 | /**
187 | * @return bool
188 | */
189 | protected static function hasCURLFileSupport()
190 | {
191 | return class_exists('CURLFile');
192 | }
193 |
194 | /**
195 | * @param string $headerString
196 | * @return string[]
197 | */
198 | protected static function splitResponseHeaders($headerString)
199 | {
200 | $results = [];
201 | $headerLines = preg_split('/[\r\n]+/', $headerString);
202 | foreach ($headerLines as $headerLine) {
203 | if (empty($headerLine)) {
204 | continue;
205 | }
206 |
207 | // Header contains HTTP version specification and path
208 | if (strpos($headerLine, 'HTTP/') === 0) {
209 | // Reset the output array as there may by multiple response headers
210 | $results = [];
211 | continue;
212 | }
213 |
214 | $results[] = "$headerLine";
215 | }
216 |
217 | return $results;
218 | }
219 |
220 | /**
221 | * Returns true if response code matches 2xx or 3xx
222 | * @param int $responseCode
223 | * @return bool
224 | */
225 | public static function isResponseCodeOk($responseCode)
226 | {
227 | return preg_match('/^[23]\d\d$/', $responseCode) === 1;
228 | }
229 |
230 | /**
231 | * @return string
232 | */
233 | protected static function getTargetUrl()
234 | {
235 | if (!empty(static::$TARGET_URL)) {
236 | $targetURL = static::$TARGET_URL;
237 | } else {
238 | $targetURL = static::ri($_SERVER[static::$HEADER_HTTP_PROXY_TARGET_URL]);
239 | }
240 |
241 | if (empty($targetURL)) {
242 | throw new RuntimeException(static::$HEADER_HTTP_PROXY_TARGET_URL . ' header is empty');
243 | }
244 |
245 | if (filter_var($targetURL, FILTER_VALIDATE_URL) === false) {
246 | throw new RuntimeException(static::$HEADER_HTTP_PROXY_TARGET_URL . ' "' . $targetURL . '" is invalid');
247 | }
248 |
249 | return $targetURL;
250 | }
251 |
252 | /**
253 | * @return bool
254 | */
255 | protected static function isDebug()
256 | {
257 | return static::$DEBUG || !empty($_SERVER[static::$HEADER_HTTP_PROXY_DEBUG]);
258 | }
259 |
260 | /**
261 | * @return bool
262 | */
263 | protected static function isAuthenticated()
264 | {
265 | return !static::$ENABLE_AUTH || static::ri($_SERVER[static::$HEADER_HTTP_PROXY_AUTH]) === static::$AUTH_KEY;
266 | }
267 |
268 | /**
269 | * @param string[] $skippedHeaders
270 | * @return string[]
271 | */
272 | protected static function getIncomingRequestHeaders($skippedHeaders = [])
273 | {
274 | $results = [];
275 | foreach ($_SERVER as $key => $value) {
276 | if (in_array($key, $skippedHeaders)) {
277 | continue;
278 | }
279 |
280 | $loweredKey = strtolower($key);
281 | if (strpos($loweredKey, 'http_') === 0) {
282 | // Remove prefix
283 | $key = substr($loweredKey, strlen('http_'));
284 | // Replace underscores with dashes
285 | $key = str_replace('_', '-', $key);
286 | // Capital each word
287 | $key = ucwords($key, '-');
288 |
289 | $results[] = "$key: $value";
290 | }
291 | }
292 |
293 | return $results;
294 | }
295 |
296 | /**
297 | * @param string $targetURL
298 | * @return false|resource
299 | */
300 | protected static function createRequest($targetURL)
301 | {
302 | $request = curl_init($targetURL);
303 |
304 | // Set input data
305 | $requestMethod = strtoupper(static::ri($_SERVER['REQUEST_METHOD']));
306 | if ($requestMethod === "PUT" || $requestMethod === "PATCH") {
307 | curl_setopt($request, CURLOPT_POSTFIELDS, file_get_contents('php://input'));
308 | } elseif ($requestMethod === "POST") {
309 | $data = array();
310 |
311 | if (!empty($_FILES)) {
312 | if (!static::hasCURLFileSupport()) {
313 | curl_setopt($request, CURLOPT_SAFE_UPLOAD, false);
314 | }
315 |
316 | foreach ($_FILES as $fileName => $file) {
317 | $filePath = realpath($file['tmp_name']);
318 |
319 | if (static::hasCURLFileSupport()) {
320 | $data[$fileName] = new CURLFile($filePath, $file['type'], $file['name']);
321 | } else {
322 | $data[$fileName] = '@' . $filePath;
323 | }
324 | }
325 | }
326 |
327 | curl_setopt($request, CURLOPT_POSTFIELDS, $data + $_POST);
328 | }
329 |
330 | $headers = static::getIncomingRequestHeaders(static::getSkippedHeaders());
331 |
332 | curl_setopt_array($request, [
333 | CURLOPT_FOLLOWLOCATION => true,
334 | CURLOPT_HEADER => static::$CURLOPT_HEADER,
335 | CURLOPT_RETURNTRANSFER => static::$CURLOPT_RETURNTRANSFER,
336 | CURLINFO_HEADER_OUT => true,
337 | CURLOPT_HTTPHEADER => $headers
338 | ]);
339 |
340 | return $request;
341 | }
342 |
343 | /**
344 | * @return int HTTP response code (200, 404, 500, etc.)
345 | */
346 | public static function run()
347 | {
348 | if (!static::isAuthenticated()) {
349 | throw new RuntimeException(static::$HEADER_HTTP_PROXY_AUTH . ' header is invalid');
350 | }
351 |
352 | $debug = static::isDebug();
353 | $targetURL = static::getTargetUrl();
354 |
355 | $request = static::createRequest($targetURL);
356 |
357 | // Get response
358 | $response = curl_exec($request);
359 |
360 | $headerSize = curl_getinfo($request, CURLINFO_HEADER_SIZE);
361 | $responseHeader = substr($response, 0, $headerSize);
362 | $responseBody = substr($response, $headerSize);
363 | $responseInfo = curl_getinfo($request);
364 | $responseCode = static::ri($responseInfo['http_code'], 500);
365 | $redirectCount = static::ri($responseInfo['redirect_count'], 0);
366 | $requestHeaders = preg_split('/[\r\n]+/', static::ri($responseInfo['request_header'], ''));
367 | if ($responseCode === 0) {
368 | $responseCode = 404;
369 | }
370 |
371 | $finalRequestURL = curl_getinfo($request, CURLINFO_EFFECTIVE_URL);
372 | if ($redirectCount > 0 && !empty($finalRequestURL)) {
373 | $finalRequestURLParts = parse_url($finalRequestURL);
374 | $effectiveURL = static::ri($finalRequestURLParts['scheme'], 'http') . '://' .
375 | static::ri($finalRequestURLParts['host']) . static::ri($finalRequestURLParts['path'], '');
376 | }
377 |
378 | curl_close($request);
379 |
380 | //----------------------------------
381 |
382 | // Split header text into an array.
383 | $responseHeaders = static::splitResponseHeaders($responseHeader);
384 | // Pass headers to output
385 | foreach ($responseHeaders as $header) {
386 | $headerParts = preg_split('/:\s+/', $header, 2);
387 | if (count($headerParts) !== 2) {
388 | throw new RuntimeException("Can not parse header \"$header\"");
389 | }
390 |
391 | $headerName = $headerParts[0];
392 | $loweredHeaderName = strtolower($headerName);
393 |
394 | $headerValue = $headerParts[1];
395 | $loweredHeaderValue = strtolower($headerValue);
396 |
397 | // Pass following headers to response
398 | if (in_array($loweredHeaderName,
399 | ['content-type', 'content-language', 'content-security', 'server'])) {
400 | header("$headerName: $headerValue");
401 | } elseif (strpos($loweredHeaderName, 'x-') === 0) {
402 | header("$headerName: $headerValue");
403 | } // Replace cookie domain and path
404 | elseif ($loweredHeaderName === 'set-cookie') {
405 | $newValue = preg_replace('/((?>domain)\s*=\s*)[^;\s]+/', '\1.' . $_SERVER['HTTP_HOST'], $headerValue);
406 | $newValue = preg_replace('/\s*;?\s*path\s*=\s*[^;\s]+/', '', $newValue);
407 | header("$headerName: $newValue", false);
408 | } // Decode response body if gzip encoding is used
409 | elseif ($loweredHeaderName === 'content-encoding' && $loweredHeaderValue === 'gzip') {
410 | $responseBody = gzdecode($responseBody);
411 | }
412 | }
413 |
414 | http_response_code($responseCode);
415 |
416 | //----------------------------------
417 |
418 | if ($debug) {
419 | echo 'Headers sent to proxy' . PHP_EOL . PHP_EOL;
420 | echo implode(PHP_EOL, static::getIncomingRequestHeaders());
421 | echo static::$HR;
422 |
423 | if (!empty($_GET)) {
424 | echo '$_GET sent to proxy' . PHP_EOL . PHP_EOL;
425 | print_r($_GET);
426 | echo static::$HR;
427 | }
428 |
429 | if (!empty($_POST)) {
430 | echo '$_POST sent to proxy' . PHP_EOL . PHP_EOL;
431 | print_r($_POST);
432 | echo static::$HR;
433 | }
434 |
435 | echo 'Headers sent to target' . PHP_EOL . PHP_EOL;
436 | echo implode(PHP_EOL, $requestHeaders);
437 | echo static::$HR;
438 |
439 | if (isset($effectiveURL) && $effectiveURL !== $targetURL) {
440 | echo "Request was redirected from \"$targetURL\" to \"$effectiveURL\"";
441 | echo static::$HR;
442 | }
443 |
444 | echo 'Headers received from target' . PHP_EOL . PHP_EOL;
445 | echo $responseHeader;
446 | echo static::$HR;
447 |
448 | echo 'Headers sent from proxy to client' . PHP_EOL . PHP_EOL;
449 | echo implode(PHP_EOL, headers_list());
450 | echo static::$HR;
451 |
452 | echo 'Body sent from proxy to client' . PHP_EOL . PHP_EOL;
453 | }
454 |
455 | echo $responseBody;
456 | return $responseCode;
457 | }
458 | }
459 |
460 | if (!Proxy::isInstalledWithComposer()) {
461 | Proxy::checkCompatibility();
462 | Proxy::registerErrorHandlers();
463 | $responseCode = Proxy::run();
464 |
465 | if (Proxy::isResponseCodeOk($responseCode)) {
466 | exit(0);
467 | } else {
468 | exit($responseCode);
469 | }
470 | }
471 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simple PHP Proxy
2 |
3 | This proxy script allows you to forward all HTTP/HTTPS requests to another server. Works for all common request types
4 | including GET, POST requests with files, PATCH and PUT requests. It has minimal set of requirements
5 | (PHP >=5.6, libcurl, gzip) which are available even on the smallest free hostings and has its own simple authorization
6 | and cookie support.
7 |
8 | ## How to use
9 | * Copy the [Proxy.php](Proxy.php) script to publicly-accessible folder of a PHP web server (the script is standalone and has no PHP dependencies)
10 | * Make a cURL request targeting this script
11 | * Add **Proxy-Auth** header with auth key [found here](https://github.com/zounar/php-proxy/blob/master/Proxy.php#L40)
12 | * Add **Proxy-Target-URL** header with URL to be requested by the proxy
13 | * (Optional) Add **Proxy-Debug** header for debug mode
14 |
15 | In order to protect using proxy by unauthorized users, consider changing `Proxy-Auth` token in [proxy source file](https://github.com/zounar/php-proxy/blob/master/Proxy.php#L40) and in all your requests.
16 |
17 | ## How to use (via composer)
18 | This might be useful when you want to redirect requests coming into your app.
19 |
20 | * Run `composer require zounar/php-proxy`
21 | * Add `Proxy::run();` line to where you want to execute it (usually into a controller action)
22 | * In this example, the script is in `AppController` - `actionProxy`:
23 | ```
24 | use Zounar\PHPProxy\Proxy;
25 |
26 | class AppController extends Controller {
27 |
28 | public function actionProxy() {
29 | Proxy::$AUTH_KEY = '';
30 | // Do your custom logic before running proxy
31 | $responseCode = Proxy::run();
32 | // Do your custom logic after running proxy
33 | // You can utilize HTTP response code returned from the run() method
34 | }
35 | }
36 | ```
37 | * Make a cURL request to your web
38 | * In the example, it would be `http://your-web.com/app/proxy`
39 | * Add **Proxy-Auth** header with auth key [found here](https://github.com/zounar/php-proxy/blob/master/Proxy.php#L40)
40 | * Add **Proxy-Target-URL** header with URL to be requested by the proxy
41 | * (Optional) Add **Proxy-Debug** header for debug mode
42 |
43 | In order to protect using proxy by unauthorized users, consider changing `Proxy-Auth` token by calling
44 | `Proxy::$AUTH_KEY = '';` before `Proxy::run()`. Then change the token in all your requests.
45 |
46 | ## Usage example
47 | Following example shows how to execute GET request to https://www.github.com. Proxy script is at http://www.foo.bar/Proxy.php. All proxy settings are kept default, the response is automatically echoed.
48 |
49 | ```php
50 | $request = curl_init('http://www.foo.bar/Proxy.php');
51 |
52 | curl_setopt($request, CURLOPT_HTTPHEADER, array(
53 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2',
54 | 'Proxy-Target-URL: https://www.github.com'
55 | ));
56 |
57 | curl_exec($request);
58 | ```
59 |
60 | ## Debugging
61 | In order to show some debug info from the proxy, add `Proxy-Debug: 1` header into the request. This will show debug info in plain-text containing request headers, response headers and response body.
62 |
63 | ```php
64 | $request = curl_init('http://www.foo.bar/Proxy.php');
65 |
66 | curl_setopt($request, CURLOPT_HTTPHEADER, array(
67 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2',
68 | 'Proxy-Target-URL: https://www.github.com',
69 | 'Proxy-Debug: 1'
70 | ));
71 |
72 | curl_exec($request);
73 | ```
74 |
75 | ## Specifying User-Agent
76 | Some sites may return different content for different user agents. In such case add `User-Agent` header to cURL request, it will be automatically passed to the request for target site. In this case it's Firefox 70 for Ubuntu.
77 |
78 | ```php
79 | $request = curl_init('http://www.foo.bar/Proxy.php');
80 |
81 | curl_setopt($request, CURLOPT_HTTPHEADER, array(
82 | 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0',
83 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2',
84 | 'Proxy-Target-URL: https://www.github.com'
85 | ));
86 |
87 | curl_exec($request);
88 | ```
89 |
90 | ## Error 301 Moved permanently
91 | It might occur that there's a redirection when calling the proxy (not the target site), eg. during `http -> https` redirection. You can either modify/fix the proxy URL (which is recommended), or add `CURLOPT_FOLLOWLOCATION` option before `curl_exec`.
92 |
93 | ```php
94 | $request = curl_init('http://www.foo.bar/Proxy.php');
95 |
96 | curl_setopt($request, CURLOPT_FOLLOWLOCATION, true );
97 | curl_setopt($request, CURLOPT_HTTPHEADER, array(
98 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2',
99 | 'Proxy-Target-URL: https://www.github.com'
100 | ));
101 |
102 | curl_exec($request);
103 | ```
104 |
105 | ## Save response into variable
106 | The default cURL behavior is to echo the response of `curl_exec`. In order to save response into variable, all you have to do is to add `CURLOPT_RETURNTRANSFER` cURL option.
107 |
108 | ```php
109 | $request = curl_init('http://www.foo.bar/Proxy.php');
110 |
111 | curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
112 | curl_setopt($request, CURLOPT_HTTPHEADER, array(
113 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2',
114 | 'Proxy-Target-URL: https://www.github.com'
115 | ));
116 |
117 | $response = curl_exec($request);
118 | ```
119 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zounar/php-proxy",
3 | "description": "Forward your HTTP/HTTPS requests to another server.",
4 | "type": "library",
5 | "keywords": [
6 | "php-proxy",
7 | "proxy-script",
8 | "php",
9 | "proxy",
10 | "http",
11 | "http-proxy",
12 | "curl",
13 | "curlphp"
14 | ],
15 | "homepage": "https://github.com/zounar/php-proxy",
16 | "readme": "README.md",
17 | "require": {
18 | "php": ">=5.6.0",
19 | "ext-curl": "*",
20 | "ext-zlib": "*"
21 | },
22 | "license": "unlicense",
23 | "authors": [
24 | {
25 | "name": "Robin Zounar",
26 | "homepage": "https://zounar.me"
27 | }
28 | ],
29 | "support": {
30 | "issues": "https://github.com/zounar/php-proxy/issues",
31 | "source": "https://github.com/zounar/php-proxy",
32 | "docs": "https://github.com/zounar/php-proxy/blob/master/README.md"
33 | },
34 | "autoload": {
35 | "psr-4": {
36 | "Zounar\\PHPProxy\\": "./"
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "a101b9e5b9b0bff50de607452cc30901",
8 | "packages": [],
9 | "packages-dev": [],
10 | "aliases": [],
11 | "minimum-stability": "stable",
12 | "stability-flags": [],
13 | "prefer-stable": false,
14 | "prefer-lowest": false,
15 | "platform": {
16 | "php": ">=5.6.0",
17 | "ext-curl": "*",
18 | "ext-zlib": "*"
19 | },
20 | "platform-dev": [],
21 | "plugin-api-version": "2.0.0"
22 | }
23 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | services:
3 | proxy:
4 | # image: webdevops/php-nginx:8.0
5 | # image: webdevops/php-nginx:7.4
6 | image: webdevops/php-nginx:5.6
7 | ports:
8 | - 80:80
9 | - 443:443
10 | volumes:
11 | - ./:/app
12 |
--------------------------------------------------------------------------------