Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.git
.github
.gitignore
.dockerignore
docker-compose.yml
Dockerfile
21 changes: 21 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: lint

on:
push:
branches: [master, revive]
pull_request:

jobs:
php-lint:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ["8.1", "8.5"]
container: php:${{ matrix.php }}-cli
steps:
- uses: actions/checkout@v4
- name: Syntax check every PHP file
run: |
set -e
find . -type f -name '*.php' -print -exec php -l {} \;
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM php:8.5-apache

RUN a2enmod rewrite

COPY . /var/www/html/

RUN chown -R www-data:www-data /var/www/html

EXPOSE 80
35 changes: 29 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,33 @@ A copy of the license is provided in this package in the filename `LICENSE.md`.

## Requirements

* PHP version > 5
* `safe_mode` turned off or at least having the `fsockopen()` function not disabled
* OpenSSL for support for secure connections (https)
* Zlib for output compression
* `file_uploads` turned On for HTTP file uploads.
* PHP >= 8.1 (tested on 8.5)
* `fsockopen()` not disabled
* OpenSSL extension for HTTPS targets
* Zlib extension for output compression (optional)
* `file_uploads` turned On for HTTP file uploads

## Installation

### Option A — Docker (recommended)

```
git clone https://github.com/PHProxy/phproxy.git
cd phproxy
docker compose up -d
```

Open http://localhost:8080/ — that's it.

The shipped `docker-compose.yml` bind-mounts the source for development.
For a production deployment, remove the `volumes:` block so the image runs
the baked-in code. The container is based on the official `php:8.5-apache`
image and needs no extra extensions.

### Option B — Standalone

Copy the files of the repository in your public web server folder or to a
directory of your liking (prefrebly in its own directory).
directory of your liking (preferably in its own directory).

```
cd /var/www/html/
Expand Down Expand Up @@ -90,3 +107,9 @@ right now.

PHProxy also doesn't support FTP. This may or may not be introduced
in future releases, but there are no current plans for FTP support.

Modern sites that gate access behind Cloudflare challenge pages, mandatory
JavaScript, or token-bound TLS (e.g. Google, GitHub, most large social
networks) will not work through PHProxy. The proxy rewrites HTML/CSS URLs
via regex — it does not run a headless browser, solve CAPTCHAs, or evaluate
client-side scripts. This is a fundamental design limitation, not a bug.
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
phproxy:
build: .
image: phproxy:local
container_name: phproxy
ports:
- "8080:80"
# Bind-mount the source for development. Remove the `volumes:` block
# below to run with the image-baked code (production mode).
volumes:
- ./:/var/www/html
restart: unless-stopped
32 changes: 16 additions & 16 deletions files/php/functions.inc.php
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<?php
function show_report($data)
function show_report(array $data): void
{
require_once "./files/php/index.inc.php";
exit(0);
}

function add_cookie($name, $value, $expires = 0)
function add_cookie(string $name, mixed $value, int $expires = 0): string
{
return rawurlencode(rawurlencode($name)) . '=' . rawurlencode(rawurlencode($value)) . (empty($expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s \G\M\T', $expires)) . '; path=/; domain=.' . $GLOBALS['_http_host'];
}

function set_post_vars($array, $parent_key = null)
function set_post_vars(array $array, ?string $parent_key = null): array
{
$temp = array();
$temp = [];

foreach ($array as $key => $value) {
$key = isset($parent_key) ? sprintf('%s[%s]', $parent_key, urlencode($key)) : urlencode($key);
Expand All @@ -26,9 +26,9 @@ function set_post_vars($array, $parent_key = null)
return $temp;
}

function set_post_files($array, $parent_key = null)
function set_post_files(array $array, ?string $parent_key = null): array
{
$temp = array();
$temp = [];

foreach ($array as $key => $value) {
$key = isset($parent_key) ? sprintf('%s[%s]', $parent_key, urlencode($key)) : urlencode($key);
Expand All @@ -42,7 +42,7 @@ function set_post_files($array, $parent_key = null)
return $temp;
}

function url_parse($url, &$container)
function url_parse(string $url, array &$container): bool
{
$temp = @parse_url($url);

Expand All @@ -57,14 +57,14 @@ function url_parse($url, &$container)
}

$temp['path'] = isset($temp['path']) ? $temp['path'] : '/';
$path = array();
$path = [];
$temp['path'] = explode('/', $temp['path']);

foreach ($temp['path'] as $dir) {
if ($dir === '..') {
array_pop($path);
} else if ($dir !== '.') {
for ($dir = rawurldecode($dir), $new_dir = '', $i = 0, $count_i = strlen($dir); $i < $count_i; $new_dir .= strspn($dir{$i}, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$-_.+!*\'(),?:@&;=') ? $dir{$i} : rawurlencode($dir{$i}), ++$i);
for ($dir = rawurldecode($dir), $new_dir = '', $i = 0, $count_i = strlen($dir); $i < $count_i; $new_dir .= strspn($dir[$i], 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$-_.+!*\'(),?:@&;=') ? $dir[$i] : rawurlencode($dir[$i]), ++$i);
$path[] = $new_dir;
}
}
Expand All @@ -82,7 +82,7 @@ function url_parse($url, &$container)
return false;
}

function complete_url($url, $proxify = true)
function complete_url(string $url, bool $proxify = true): string
{
$url = html_entity_decode(trim($url));

Expand Down Expand Up @@ -128,7 +128,7 @@ function complete_url($url, $proxify = true)
return $proxify ? "{$GLOBALS['_script_url']}?{$GLOBALS['_config']['url_var_name']}=" . encode_url($url) . $fragment : $url;
}

function proxify_inline_css($css)
function proxify_inline_css(string $css): string
{
preg_match_all('#url\s*\(\s*(.+?(?=\)[f;,}!\s*]))\)#i', $css, $matches, PREG_SET_ORDER);

Expand All @@ -139,7 +139,7 @@ function proxify_inline_css($css)
return $css;
}

function proxify_css($css)
function proxify_css(string $css): string
{
$css = proxify_inline_css($css);

Expand All @@ -160,7 +160,7 @@ function proxify_css($css)
return $css;
}

function proxify_css_url($url)
function proxify_css_url(string $url): string
{
$url = trim($url);
$delim = strpos($url, '"') === 0 ? '"' : (strpos($url, "'") === 0 ? "'" : '');
Expand All @@ -182,7 +182,7 @@ function proxify_css_url($url)
return $delim . preg_replace('#([\(\),\s\'"\\\])#', '\\$1', complete_url(trim(preg_replace('#\\\(.)#', '$1', $url)))) . $delim;
}

function encode_url($url)
function encode_url(string $url): string
{
global $_flags;

Expand All @@ -195,7 +195,7 @@ function encode_url($url)
return rawurlencode($url);
}

function decode_url($url)
function decode_url(string $url): string
{
global $_flags;
$url = rawurldecode($url);
Expand All @@ -206,5 +206,5 @@ function decode_url($url)
$url = base64_decode($url);
}

return str_replace(array('&amp;', '&#38;'), '&', $url);
return str_replace(['&amp;', '&#38;'], '&', $url);
}
2 changes: 1 addition & 1 deletion files/php/idna.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ protected function encodePart($input)
$code = $t + (($q - $t) % (static::BASE - $t));
$output .= static::$encodeTable[$code];

$q = ($q - $t) / (static::BASE - $t);
$q = intdiv($q - $t, static::BASE - $t);
}

$output .= static::$encodeTable[$q];
Expand Down
Loading
Loading