create-prisma-php-app 4.3.9 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,10 @@ require_once __DIR__ . '/settings/paths.php';
8
8
  use Dotenv\Dotenv;
9
9
  use Lib\Middleware\CorsMiddleware;
10
10
 
11
- Dotenv::createImmutable(DOCUMENT_PATH)->load();
11
+ if (file_exists(DOCUMENT_PATH . '/.env')) {
12
+ Dotenv::createImmutable(DOCUMENT_PATH)->safeLoad();
13
+ }
14
+
12
15
  CorsMiddleware::handle();
13
16
 
14
17
  if (session_status() === PHP_SESSION_NONE) {
@@ -27,6 +30,7 @@ use PP\ErrorHandler;
27
30
  use PP\Attributes\Exposed;
28
31
  use PP\Streaming\SSE;
29
32
  use PP\Security\RateLimiter;
33
+ use PP\Env;
30
34
 
31
35
  final class Bootstrap extends RuntimeException
32
36
  {
@@ -59,7 +63,7 @@ final class Bootstrap extends RuntimeException
59
63
 
60
64
  public static function run(): void
61
65
  {
62
- date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'UTC');
66
+ date_default_timezone_set(Env::string('APP_TIMEZONE', 'UTC'));
63
67
 
64
68
  PrismaPHPSettings::init();
65
69
  Request::init();
@@ -139,7 +143,7 @@ final class Bootstrap extends RuntimeException
139
143
 
140
144
  private static function setCsrfCookie(): void
141
145
  {
142
- $secret = $_ENV['FUNCTION_CALL_SECRET'] ?? 'pp_default_insecure_secret';
146
+ $secret = Env::string('FUNCTION_CALL_SECRET', 'pp_default_insecure_secret');
143
147
  $shouldRegenerate = true;
144
148
 
145
149
  if (isset($_COOKIE['prisma_php_csrf'])) {
@@ -175,7 +179,7 @@ final class Bootstrap extends RuntimeException
175
179
  {
176
180
  $headerToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
177
181
  $cookieToken = $_COOKIE['prisma_php_csrf'] ?? '';
178
- $secret = $_ENV['FUNCTION_CALL_SECRET'] ?? '';
182
+ $secret = Env::string('FUNCTION_CALL_SECRET', '');
179
183
 
180
184
  if (empty($headerToken) || empty($cookieToken)) {
181
185
  self::jsonExit(['success' => false, 'error' => 'CSRF token missing']);
@@ -217,12 +221,11 @@ final class Bootstrap extends RuntimeException
217
221
 
218
222
  private static function determineContentToInclude(): array
219
223
  {
220
- $requestUri = $_SERVER['REQUEST_URI'];
221
- $requestUri = empty($_SERVER['SCRIPT_URL']) ? trim(self::uriExtractor($requestUri)) : trim($requestUri);
224
+ $requestUri = $_SERVER['REQUEST_URI'] ?? '/';
225
+ $requestUri = trim(self::uriExtractor($requestUri));
222
226
 
223
227
  $scriptUrl = explode('?', $requestUri, 2)[0];
224
- $pathname = $_SERVER['SCRIPT_URL'] ?? $scriptUrl;
225
- $pathname = trim($pathname, '/');
228
+ $pathname = trim($scriptUrl, '/');
226
229
  $baseDir = APP_PATH;
227
230
  $includePath = '';
228
231
  $layoutsToInclude = [];
@@ -520,7 +523,7 @@ final class Bootstrap extends RuntimeException
520
523
  {
521
524
  $projectName = PrismaPHPSettings::$option->projectName ?? '';
522
525
  if (empty($projectName)) {
523
- return "/";
526
+ return $scriptUrl;
524
527
  }
525
528
 
526
529
  $escapedIdentifier = preg_quote($projectName, '/');
@@ -528,7 +531,7 @@ final class Bootstrap extends RuntimeException
528
531
  return rtrim(ltrim($matches[1], '/'), '/');
529
532
  }
530
533
 
531
- return "/";
534
+ return $scriptUrl;
532
535
  }
533
536
 
534
537
  private static function findGroupFolder(string $pathname): string
@@ -713,7 +716,7 @@ final class Bootstrap extends RuntimeException
713
716
 
714
717
  private static function checkForDuplicateRoutes(): void
715
718
  {
716
- if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'production') {
719
+ if (Env::string('APP_ENV', 'production') === 'production') {
717
720
  return;
718
721
  }
719
722
 
@@ -958,9 +961,9 @@ final class Bootstrap extends RuntimeException
958
961
 
959
962
  if (empty($limits)) {
960
963
  if ($attribute->requiresAuth) {
961
- $limits = $_ENV['RATE_LIMIT_AUTH'] ?? '60/minute';
964
+ $limits = Env::string('RATE_LIMIT_AUTH', '60/minute');
962
965
  } else {
963
- $limits = $_ENV['RATE_LIMIT_RPC'] ?? '60/minute';
966
+ $limits = Env::string('RATE_LIMIT_RPC', '60/minute');
964
967
  }
965
968
  }
966
969
 
@@ -976,6 +979,7 @@ final class Bootstrap extends RuntimeException
976
979
  return ['success' => false, 'error' => 'Function not callable from client'];
977
980
  }
978
981
 
982
+ $attribute = self::getExposedAttribute($fn);
979
983
  if (!self::validateAccess($attribute)) {
980
984
  return ['success' => false, 'error' => 'Permission denied'];
981
985
  }
@@ -996,7 +1000,7 @@ final class Bootstrap extends RuntimeException
996
1000
  return ['success' => false, 'error' => $e->getMessage()];
997
1001
  }
998
1002
 
999
- if (isset($_ENV['SHOW_ERRORS']) && $_ENV['SHOW_ERRORS'] === 'false') {
1003
+ if (Env::string('SHOW_ERRORS', 'false') === 'false') {
1000
1004
  return ['success' => false, 'error' => 'An error occurred. Please try again later.'];
1001
1005
  } else {
1002
1006
  return ['success' => false, 'error' => "Function error: {$e->getMessage()}"];
@@ -1071,7 +1075,7 @@ final class Bootstrap extends RuntimeException
1071
1075
  return ['success' => false, 'error' => $e->getMessage()];
1072
1076
  }
1073
1077
 
1074
- if (isset($_ENV['SHOW_ERRORS']) && $_ENV['SHOW_ERRORS'] === 'false') {
1078
+ if (Env::string('SHOW_ERRORS', 'false') === 'false') {
1075
1079
  return ['success' => false, 'error' => 'An error occurred. Please try again later.'];
1076
1080
  } else {
1077
1081
  return ['success' => false, 'error' => "Call error: {$e->getMessage()}"];
@@ -1319,16 +1323,20 @@ try {
1319
1323
  }
1320
1324
 
1321
1325
  if (Request::$isWire && !Bootstrap::$secondRequestC69CD) {
1322
- ob_end_clean();
1326
+ while (ob_get_level() > 0) {
1327
+ ob_end_clean();
1328
+ }
1323
1329
  Bootstrap::wireCallback();
1324
1330
  }
1325
1331
 
1326
1332
  if ((!Request::$isWire && !Bootstrap::$secondRequestC69CD) && isset(Bootstrap::$requestFilesData[Request::$decodedUri])) {
1333
+ $cacheEnabled = (Env::string('CACHE_ENABLED', 'false') === 'true');
1334
+
1327
1335
  $shouldCache = CacheHandler::$isCacheable === true
1328
- || (CacheHandler::$isCacheable === null && $_ENV['CACHE_ENABLED'] === 'true');
1336
+ || (CacheHandler::$isCacheable === null && $cacheEnabled);
1329
1337
 
1330
1338
  if ($shouldCache) {
1331
- CacheHandler::serveCache(Request::$decodedUri, intval($_ENV['CACHE_TTL'] ?? 600));
1339
+ CacheHandler::serveCache(Request::$decodedUri, intval(Env::string('CACHE_TTL', '600')));
1332
1340
  }
1333
1341
  }
1334
1342
 
@@ -15,11 +15,12 @@ use PP\Request;
15
15
  use Exception;
16
16
  use InvalidArgumentException;
17
17
  use ArrayObject;
18
+ use PP\Env;
18
19
 
19
20
  class Auth
20
21
  {
21
22
  public const PAYLOAD_NAME = 'payload_name_8639D';
22
- public const ROLE_NAME = '';
23
+ public const ROLE_NAME = 'role';
23
24
  public const PAYLOAD_SESSION_KEY = 'payload_session_key_2183A';
24
25
 
25
26
  public static string $cookieName = '';
@@ -27,11 +28,11 @@ class Auth
27
28
  private static ?Auth $instance = null;
28
29
  private const PPAUTH = 'ppauth';
29
30
  private string $secretKey;
30
- private string $defaultTokenValidity = '1h'; // Default to 1 hour
31
+ private string $defaultTokenValidity = AuthConfig::DEFAULT_TOKEN_VALIDITY;
31
32
 
32
33
  private function __construct()
33
34
  {
34
- $this->secretKey = $_ENV['AUTH_SECRET'] ?? 'CD24eEv4qbsC5LOzqeaWbcr58mBMSvA4Mkii8GjRiHkt';
35
+ $this->secretKey = Env::string('AUTH_SECRET', 'CD24eEv4qbsC5LOzqeaWbcr58mBMSvA4Mkii8GjRiHkt');
35
36
  self::$cookieName = self::getCookieName();
36
37
  }
37
38
 
@@ -255,7 +256,7 @@ class Auth
255
256
 
256
257
  public function rotateCsrfToken(): void
257
258
  {
258
- $secret = $_ENV['FUNCTION_CALL_SECRET'] ?? '';
259
+ $secret = Env::string('FUNCTION_CALL_SECRET', '');
259
260
 
260
261
  if (empty($secret)) {
261
262
  return;
@@ -546,7 +547,7 @@ class Auth
546
547
 
547
548
  private static function getCookieName(): string
548
549
  {
549
- $authCookieName = $_ENV['AUTH_COOKIE_NAME'] ?? 'auth_cookie_name_d36e5';
550
+ $authCookieName = Env::string('AUTH_COOKIE_NAME', 'auth_cookie_name_d36e5');
550
551
  return strtolower(preg_replace('/\s+/', '_', trim($authCookieName)));
551
552
  }
552
553
  }
@@ -23,6 +23,7 @@ final class AuthConfig
23
23
  public const IS_ROLE_BASE = false;
24
24
  public const IS_TOKEN_AUTO_REFRESH = false;
25
25
  public const IS_ALL_ROUTES_PRIVATE = false;
26
+ public const DEFAULT_TOKEN_VALIDITY = "1h"; // Default to 1 hour
26
27
 
27
28
  /**
28
29
  * This is the (default) option for authentication. If IS_ALL_ROUTES_PRIVATE is set to false,
@@ -12,20 +12,21 @@ use Dotenv\Dotenv;
12
12
  use PhpMcp\Server\Server;
13
13
  use PhpMcp\Server\Transports\StreamableHttpServerTransport;
14
14
  use Throwable;
15
+ use PP\Env;
15
16
 
16
17
  // ── Load .env (optional) and timezone ──────────────────────────────────────────
17
18
  if (file_exists(DOCUMENT_PATH . '/.env')) {
18
19
  Dotenv::createImmutable(DOCUMENT_PATH)->safeLoad();
19
20
  }
20
- date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'UTC');
21
+ date_default_timezone_set(Env::string('APP_TIMEZONE', 'UTC'));
21
22
 
22
23
  // ── Resolve settings (with sane defaults) ─────────────────────────────────────
23
- $appName = $_ENV['MCP_NAME'] ?? 'prisma-php-mcp';
24
- $appVersion = $_ENV['MCP_VERSION'] ?? '0.0.1';
25
- $host = $_ENV['MCP_HOST'] ?? '127.0.0.1';
26
- $port = (int)($_ENV['MCP_PORT'] ?? 4000);
27
- $prefix = trim($_ENV['MCP_PATH_PREFIX'] ?? 'mcp', '/');
28
- $enableJson = filter_var($_ENV['MCP_JSON_RESPONSE'] ?? 'false', FILTER_VALIDATE_BOOLEAN);
24
+ $appName = Env::string('MCP_NAME', 'prisma-php-mcp');
25
+ $appVersion = Env::string('MCP_VERSION', '0.0.1');
26
+ $host = Env::string('MCP_HOST', '127.0.0.1');
27
+ $port = Env::int('MCP_PORT', 4000);
28
+ $prefix = trim(Env::string('MCP_PATH_PREFIX', 'mcp'), '/');
29
+ $enableJson = Env::bool('MCP_JSON_RESPONSE', false);
29
30
 
30
31
  // ── Build server and discover tools ───────────────────────────────────────────
31
32
  $server = Server::make()
@@ -4,6 +4,8 @@ declare(strict_types=1);
4
4
 
5
5
  namespace Lib\Middleware;
6
6
 
7
+ use PP\Env;
8
+
7
9
  final class CorsMiddleware
8
10
  {
9
11
  public static function handle(?array $overrides = null): void
@@ -58,14 +60,14 @@ final class CorsMiddleware
58
60
 
59
61
  private static function buildConfig(?array $overrides): array
60
62
  {
61
- $allowed = self::parseList($_ENV['CORS_ALLOWED_ORIGINS'] ?? '');
63
+ $allowed = self::parseList(Env::string('CORS_ALLOWED_ORIGINS', ''));
62
64
  $cfg = [
63
65
  'allowedOrigins' => $allowed,
64
- 'allowCredentials' => filter_var($_ENV['CORS_ALLOW_CREDENTIALS'] ?? 'false', FILTER_VALIDATE_BOOLEAN),
65
- 'allowedMethods' => $_ENV['CORS_ALLOWED_METHODS'] ?? 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
66
- 'allowedHeaders' => trim($_ENV['CORS_ALLOWED_HEADERS'] ?? ''),
67
- 'exposeHeaders' => trim($_ENV['CORS_EXPOSE_HEADERS'] ?? ''),
68
- 'maxAge' => (int)($_ENV['CORS_MAX_AGE'] ?? 86400),
66
+ 'allowCredentials' => Env::bool('CORS_ALLOW_CREDENTIALS', false),
67
+ 'allowedMethods' => Env::string('CORS_ALLOWED_METHODS', 'GET, POST, PUT, PATCH, DELETE, OPTIONS'),
68
+ 'allowedHeaders' => trim(Env::string('CORS_ALLOWED_HEADERS', '')),
69
+ 'exposeHeaders' => trim(Env::string('CORS_EXPOSE_HEADERS', '')),
70
+ 'maxAge' => Env::int('CORS_MAX_AGE', 86400),
69
71
  ];
70
72
 
71
73
  if (is_array($overrides)) {
@@ -15,12 +15,12 @@ class ConnectionManager implements MessageComponentInterface
15
15
 
16
16
  public function __construct()
17
17
  {
18
- $this->clients = new SplObjectStorage;
18
+ $this->clients = new SplObjectStorage();
19
19
  }
20
20
 
21
21
  public function onOpen(ConnectionInterface $conn): void
22
22
  {
23
- $this->clients->attach($conn);
23
+ $this->clients->offsetSet($conn, true);
24
24
  echo "New connection! ({$conn->resourceId})";
25
25
  }
26
26
 
@@ -35,7 +35,7 @@ class ConnectionManager implements MessageComponentInterface
35
35
 
36
36
  public function onClose(ConnectionInterface $conn): void
37
37
  {
38
- $this->clients->detach($conn);
38
+ $this->clients->offsetUnset($conn);
39
39
  echo "Connection {$conn->resourceId} has disconnected";
40
40
  }
41
41
 
@@ -15,14 +15,15 @@ use Ratchet\WebSocket\WsServer;
15
15
  use Lib\Websocket\ConnectionManager;
16
16
  use React\EventLoop\LoopInterface;
17
17
  use Throwable;
18
+ use PP\Env;
18
19
 
19
20
  // ── Load .env (optional) and timezone ─────────────────────────────────────────
20
21
  if (file_exists(DOCUMENT_PATH . '/.env')) {
21
22
  Dotenv::createImmutable(DOCUMENT_PATH)->safeLoad();
22
23
  }
23
- date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'UTC');
24
+ date_default_timezone_set(Env::string('APP_TIMEZONE', 'UTC'));
24
25
 
25
- // ── Tiny argv parser: allows --host=0.0.0.0 --port=8080 ──────────────────────
26
+ // ── Tiny argv parser: allows --host=0.0.0.0 --port=9001 ──────────────────────
26
27
  $cli = [];
27
28
  foreach ($argv ?? [] as $arg) {
28
29
  if (preg_match('/^--([^=]+)=(.*)$/', $arg, $m)) {
@@ -31,11 +32,11 @@ foreach ($argv ?? [] as $arg) {
31
32
  }
32
33
 
33
34
  // ── Resolve settings (env → cli defaults) ────────────────────────────────────
34
- $appName = $_ENV['WS_NAME'] ?? 'prisma-php-ws';
35
- $appVer = $_ENV['WS_VERSION'] ?? '0.0.1';
36
- $host = $cli['host'] ?? ($_ENV['WS_HOST'] ?? '127.0.0.1');
37
- $port = (int)($cli['port'] ?? ($_ENV['WS_PORT'] ?? 8080));
38
- $verbose = filter_var($cli['verbose'] ?? ($_ENV['WS_VERBOSE'] ?? 'true'), FILTER_VALIDATE_BOOLEAN);
35
+ $appName = Env::string('WS_NAME', 'prisma-php-ws');
36
+ $appVer = Env::string('WS_VERSION', '0.0.1');
37
+ $host = $cli['host'] ?? Env::string('WS_HOST', '127.0.0.1');
38
+ $port = (int)($cli['port'] ?? Env::int('WS_PORT', 9001));
39
+ $verbose = filter_var($cli['verbose'] ?? Env::bool('WS_VERBOSE', true), FILTER_VALIDATE_BOOLEAN);
39
40
 
40
41
  // ── Console helpers ──────────────────────────────────────────────────────────
41
42
  $color = static fn(string $t, string $c) => "\033[{$c}m{$t}\033[0m";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "4.3.9",
3
+ "version": "4.4.0",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",