Author: Shivam Mathur (shivammathur)
Date: 2024-07-08T00:27:31+05:30

Commit: 
https://github.com/php/web-downloads/commit/2dd6a7cc2c19c5ce43d840541ebf6ca5eba55871
Raw diff: 
https://github.com/php/web-downloads/commit/2dd6a7cc2c19c5ce43d840541ebf6ca5eba55871.diff

Add routing and Auth

Changed paths:
  A  .env.example
  A  .gitignore
  A  bootstrap.php
  A  composer.json
  A  composer.lock
  A  public/index.php
  A  routes.php
  A  src/Auth.php
  A  src/IndexHandler.php
  A  src/PeclHandler.php
  A  src/PhpHandler.php
  A  src/Router.php
  A  src/Validator.php
  A  src/WinlibsHandler.php
  A  src/scripts/common.sh
  A  src/scripts/pecl.sh
  A  src/scripts/php.sh
  A  src/scripts/winlibs.sh
  D  src/pecl.sh
  D  src/php.sh
  D  src/run.php
  D  src/winlibs.sh


Diff:

diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..e64e685
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,3 @@
+AUTH_TOKEN=
+BUILDS_DIRECTORY=
+SCRIPTS_USER=
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3d5163b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/vendor/
+.env
diff --git a/bootstrap.php b/bootstrap.php
new file mode 100644
index 0000000..54248d2
--- /dev/null
+++ b/bootstrap.php
@@ -0,0 +1,10 @@
+<?php
+
+require_once __DIR__.'/vendor/autoload.php';
+
+use Dotenv\Dotenv;
+
+$dotenv = Dotenv::createImmutable(__DIR__);
+$dotenv->load();
+
+require_once __DIR__.'/routes.php';
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..89846d4
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,16 @@
+{
+    "name": "php/web-downloads",
+    "description": "Scripts for downloads.php.net",
+    "type": "project",
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "App\\": "src/"
+        }
+    },
+    "require": {
+        "php": "^8.2",
+        "vlucas/phpdotenv": "^5.6",
+        "symfony/process": "^7.1"
+    }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..6fc5314
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,542 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at 
https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies";,
+        "This file is @generated automatically"
+    ],
+    "content-hash": "4e249ba8959f6a2c56b273e97dedc5a5",
+    "packages": [
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git";,
+                "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862";,
+                "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "he...@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell";
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": 
"https://github.com/GrahamCampbell/Result-Type/issues";,
+                "source": 
"https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2";
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/graham-campbell/result-type";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-11-12T22:16:48+00:00"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.9.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git";,
+                "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820";,
+                "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": true
+                },
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmitt...@gmail.com",
+                    "homepage": "https://github.com/schmittjoh";
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "he...@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell";
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues";,
+                "source": "https://github.com/schmittjoh/php-option/tree/1.9.2";
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/phpoption/phpoption";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-11-12T21:59:55+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.30.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git";,
+                "reference": "0424dff1c58f028c451efff2045f5d92410bd540"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540";,
+                "reference": "0424dff1c58f028c451efff2045f5d92410bd540",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill";
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "backend...@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors";
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com";,
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": 
"https://github.com/symfony/polyfill-ctype/tree/v1.30.0";
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor";,
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/symfony/symfony";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-05-31T15:07:36+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.30.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git";,
+                "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c";,
+                "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill";
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p...@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors";
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com";,
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": 
"https://github.com/symfony/polyfill-mbstring/tree/v1.30.0";
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor";,
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/symfony/symfony";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-06-19T12:30:46+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.30.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git";,
+                "reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433";,
+                "reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill";
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.ba...@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p...@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors";
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ 
features to lower PHP versions",
+            "homepage": "https://symfony.com";,
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": 
"https://github.com/symfony/polyfill-php80/tree/v1.30.0";
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor";,
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/symfony/symfony";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-05-31T15:07:36+00:00"
+        },
+        {
+            "name": "symfony/process",
+            "version": "v7.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git";,
+                "reference": "febf90124323a093c7ee06fdb30e765ca3c20028"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/symfony/process/zipball/febf90124323a093c7ee06fdb30e765ca3c20028";,
+                "reference": "febf90124323a093c7ee06fdb30e765ca3c20028",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Process\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fab...@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors";
+                }
+            ],
+            "description": "Executes commands in sub-processes",
+            "homepage": "https://symfony.com";,
+            "support": {
+                "source": "https://github.com/symfony/process/tree/v7.1.1";
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor";,
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/symfony/symfony";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-05-31T14:57:53+00:00"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git";,
+                "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4";,
+                "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.1.2",
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.2",
+                "symfony/polyfill-ctype": "^1.24",
+                "symfony/polyfill-mbstring": "^1.24",
+                "symfony/polyfill-php80": "^1.24"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": true
+                },
+                "branch-alias": {
+                    "dev-master": "5.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "he...@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell";
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "va...@vancelucas.com",
+                    "homepage": "https://github.com/vlucas";
+                }
+            ],
+            "description": "Loads environment variables from `.env` to 
`getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues";,
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0";
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell";,
+                    "type": "github"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/vlucas/phpdotenv";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-11-12T22:43:29+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": "^8.2"
+    },
+    "platform-dev": [],
+    "plugin-api-version": "2.6.0"
+}
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..f2fe731
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,7 @@
+<?php
+
+// Register the Composer autoloader...
+require_once __DIR__.'/../vendor/autoload.php';
+
+// Init
+require_once __DIR__ . '/../bootstrap.php';
diff --git a/routes.php b/routes.php
new file mode 100644
index 0000000..dbd8441
--- /dev/null
+++ b/routes.php
@@ -0,0 +1,11 @@
+<?php
+
+use App\IndexHandler;
+use App\Router;
+
+$router = new Router();
+$router->registerRoute('/', 'GET', IndexHandler::class);
+$router->registerRoute('/pecl', 'POST', PeclHandler::class, true);
+$router->registerRoute('/winlibs', 'POST', WinlibsHandler::class, true);
+$router->registerRoute('/php', 'POST', PhpHandler::class, true);
+$router->handleRequest();
diff --git a/src/Auth.php b/src/Auth.php
new file mode 100644
index 0000000..26dd4b6
--- /dev/null
+++ b/src/Auth.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace App;
+
+class Auth
+{
+    public function authenticate(): bool
+    {
+        $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
+        $authToken = str_replace('Bearer ', '', $authHeader);
+
+        return $authToken === $_ENV['AUTH_TOKEN'];
+    }
+}
diff --git a/src/IndexHandler.php b/src/IndexHandler.php
new file mode 100644
index 0000000..c15db73
--- /dev/null
+++ b/src/IndexHandler.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace App;
+class IndexHandler
+{
+    public function handle(): void
+    {
+        echo 'Welcome!';
+    }
+}
\ No newline at end of file
diff --git a/src/PeclHandler.php b/src/PeclHandler.php
new file mode 100644
index 0000000..0310c1d
--- /dev/null
+++ b/src/PeclHandler.php
@@ -0,0 +1,9 @@
+<?php
+
+class PeclHandler
+{
+
+    public function handle()
+    {
+    }
+}
\ No newline at end of file
diff --git a/src/PhpHandler.php b/src/PhpHandler.php
new file mode 100644
index 0000000..61ffbc3
--- /dev/null
+++ b/src/PhpHandler.php
@@ -0,0 +1,9 @@
+<?php
+
+class PhpHandler
+{
+
+    public function handle()
+    {
+    }
+}
\ No newline at end of file
diff --git a/src/Router.php b/src/Router.php
new file mode 100644
index 0000000..7d16d5f
--- /dev/null
+++ b/src/Router.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace App;
+
+class Router
+{
+    private array $routes = [];
+    private Auth $auth;
+
+    public function __construct()
+    {
+        $this->auth = new Auth();
+    }
+
+    public function registerRoute(string $path, string $method, string 
$handler, bool $requiresAuth = false): void
+    {
+        $this->routes[$method][$path] = [
+            'handler' => $handler,
+            'requiresAuth' => $requiresAuth
+        ];
+    }
+
+    public function handleRequest(): void
+    {
+        $path = $_SERVER['REQUEST_URI'];
+        $method = $_SERVER['REQUEST_METHOD'];
+
+        $allowedMethods = [];
+
+        foreach ($this->routes as $registeredMethod => $paths) {
+            if (isset($paths[$path])) {
+                $allowedMethods[] = $registeredMethod;
+            }
+        }
+
+        if (isset($this->routes[$method][$path])) {
+            $route = $this->routes[$method][$path];
+
+            if ($route['requiresAuth'] && !$this->auth->authenticate()) {
+                http_response_code(401);
+                echo 'Unauthorized';
+                return;
+            }
+
+            $handler = $route['handler'];
+            (new $handler)->handle();
+        } elseif (!empty($allowedMethods)) {
+            http_response_code(405);
+            echo 'Method Not Allowed. Allowed methods: ' . implode(', ', 
$allowedMethods);
+        } else {
+            http_response_code(404);
+            echo 'Not Found';
+        }
+    }
+}
diff --git a/src/Validator.php b/src/Validator.php
new file mode 100644
index 0000000..345ac19
--- /dev/null
+++ b/src/Validator.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace App;
+
+class Validator
+{
+    protected array $errors = [];
+
+    public function validate(array $data, array $rules): bool
+    {
+        $this->errors = [];
+
+        foreach ($rules as $field => $ruleString) {
+            $rulesArray = explode('|', $ruleString);
+            foreach ($rulesArray as $rule) {
+                $ruleParts = explode(':', $rule);
+                $ruleName = $ruleParts[0];
+                $ruleValue = $ruleParts[1] ?? null;
+
+                if (!$this->$ruleName($data[$field] ?? null, $ruleValue)) {
+                    $this->errors[$field][] = $this->getErrorMessage($field, 
$ruleName);
+                }
+            }
+        }
+
+        return empty($this->errors);
+    }
+
+    public function errors(): array
+    {
+        return $this->errors;
+    }
+
+    protected function required($value): bool
+    {
+        return !is_null($value) && $value !== '';
+    }
+
+    protected function url($value): bool
+    {
+        return filter_var($value, FILTER_VALIDATE_URL) !== false;
+    }
+
+    protected function string($value): bool
+    {
+        return is_string($value);
+    }
+
+    protected function getErrorMessage($field, $rule): string
+    {
+        $messages = [
+            'required' => "The $field field is required.",
+            'url' => "The $field field must be a valid URL.",
+            'string' => "The $field field must be a string.",
+        ];
+
+        return $messages[$rule] ?? "The $field field has an invalid value.";
+    }
+}
diff --git a/src/WinlibsHandler.php b/src/WinlibsHandler.php
new file mode 100644
index 0000000..8af13c8
--- /dev/null
+++ b/src/WinlibsHandler.php
@@ -0,0 +1,9 @@
+<?php
+
+class WinlibsHandler
+{
+
+    public function handle()
+    {
+    }
+}
\ No newline at end of file
diff --git a/src/pecl.sh b/src/pecl.sh
deleted file mode 100644
index 544ce32..0000000
--- a/src/pecl.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-# Fetch PECL extension builds
diff --git a/src/run.php b/src/run.php
deleted file mode 100644
index 215a1fb..0000000
--- a/src/run.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-
-// TODO: Implement the run function
\ No newline at end of file
diff --git a/src/scripts/common.sh b/src/scripts/common.sh
new file mode 100644
index 0000000..b9c4c94
--- /dev/null
+++ b/src/scripts/common.sh
@@ -0,0 +1,10 @@
+function fetch_artifact() {
+  local filepath=$1
+  local url=$2
+  local token=$3
+  if [[ "$url" == *api.github.com* ]]; then
+    curl -H "Accept: application/vnd.github+json" -H "Authorization: token 
$token" -L -o "$filepath" "$url"
+  else
+    curl -L -o "$filepath" "$url"
+  fi
+}
\ No newline at end of file
diff --git a/src/scripts/pecl.sh b/src/scripts/pecl.sh
new file mode 100644
index 0000000..222a175
--- /dev/null
+++ b/src/scripts/pecl.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+# TODO: Fetch PECL builds
diff --git a/src/php.sh b/src/scripts/php.sh
similarity index 100%
rename from src/php.sh
rename to src/scripts/php.sh
diff --git a/src/winlibs.sh b/src/scripts/winlibs.sh
similarity index 100%
rename from src/winlibs.sh
rename to src/scripts/winlibs.sh

Reply via email to