create-prisma-php-app 1.19.500 → 1.20.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.
- package/.vscode/settings.json +4 -0
- package/dist/bootstrap.php +12 -8
- package/dist/index.js +1 -1025
- package/dist/prisma-client-php/index.enc +1 -1
- package/dist/settings/request-methods.php +1 -0
- package/dist/src/Lib/AI/ChatGPTClient.php +57 -34
- package/dist/src/Lib/Auth/Auth.php +31 -11
- package/dist/src/Lib/Auth/AuthConfig.php +2 -11
- package/dist/src/Lib/Middleware/AuthMiddleware.php +3 -5
- package/dist/src/Lib/Prisma/Classes/Utility.php +1 -1
- package/dist/src/Lib/StateManager.php +11 -5
- package/dist/src/Lib/Validator.php +223 -1
- package/package.json +1 -1
|
@@ -32,6 +32,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
|
|
|
32
32
|
$domainName = $_SERVER['HTTP_HOST'];
|
|
33
33
|
$scriptName = dirname($_SERVER['SCRIPT_NAME']) . '/';
|
|
34
34
|
$baseUrl = $protocol . $domainName . rtrim($scriptName, '/') . '/src/app/';
|
|
35
|
+
$referer = $_SERVER['HTTP_REFERER'] ?? 'Unknown';
|
|
35
36
|
|
|
36
37
|
$params = [];
|
|
37
38
|
|
|
@@ -14,14 +14,15 @@ class ChatGPTClient
|
|
|
14
14
|
private $client;
|
|
15
15
|
private $apiUrl;
|
|
16
16
|
private $apiKey;
|
|
17
|
+
private $cache = [];
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Constructor initializes the Guzzle HTTP client and sets up API configuration.
|
|
20
21
|
*/
|
|
21
|
-
public function __construct()
|
|
22
|
+
public function __construct(Client $client = null)
|
|
22
23
|
{
|
|
23
|
-
// Initialize the Guzzle HTTP client
|
|
24
|
-
$this->client = new Client();
|
|
24
|
+
// Initialize the Guzzle HTTP client, allowing for dependency injection
|
|
25
|
+
$this->client = $client ?: new Client();
|
|
25
26
|
|
|
26
27
|
// API URL for chat completions
|
|
27
28
|
$this->apiUrl = 'https://api.openai.com/v1/chat/completions';
|
|
@@ -36,9 +37,8 @@ class ChatGPTClient
|
|
|
36
37
|
* @param array $conversationHistory The conversation history array.
|
|
37
38
|
* @return string The model name to be used.
|
|
38
39
|
*/
|
|
39
|
-
|
|
40
|
+
protected function determineModel(array $conversationHistory): string
|
|
40
41
|
{
|
|
41
|
-
// Example logic for model selection
|
|
42
42
|
$messageCount = count($conversationHistory);
|
|
43
43
|
$totalTokens = array_reduce($conversationHistory, function ($carry, $item) {
|
|
44
44
|
return $carry + str_word_count($item['content'] ?? '');
|
|
@@ -53,6 +53,25 @@ class ChatGPTClient
|
|
|
53
53
|
return 'gpt-3.5-turbo';
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Formats the conversation history to ensure it is valid.
|
|
58
|
+
*
|
|
59
|
+
* @param array $conversationHistory The conversation history array.
|
|
60
|
+
* @return array The formatted conversation history.
|
|
61
|
+
*/
|
|
62
|
+
protected function formatConversationHistory(array $conversationHistory): array
|
|
63
|
+
{
|
|
64
|
+
$formattedHistory = [];
|
|
65
|
+
foreach ($conversationHistory as $message) {
|
|
66
|
+
if (is_array($message) && isset($message['role'], $message['content']) && Validator::string($message['content'])) {
|
|
67
|
+
$formattedHistory[] = $message;
|
|
68
|
+
} else {
|
|
69
|
+
$formattedHistory[] = ['role' => 'user', 'content' => (string) $message];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return $formattedHistory;
|
|
73
|
+
}
|
|
74
|
+
|
|
56
75
|
/**
|
|
57
76
|
* Sends a message to the OpenAI API and returns the AI's response.
|
|
58
77
|
*
|
|
@@ -64,31 +83,29 @@ class ChatGPTClient
|
|
|
64
83
|
*/
|
|
65
84
|
public function sendMessage(array $conversationHistory, string $userMessage): string
|
|
66
85
|
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
86
|
+
if (!Validator::string($userMessage)) {
|
|
87
|
+
throw new \InvalidArgumentException("Invalid user message: must be a string.");
|
|
88
|
+
}
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Ensure conversationHistory is properly formatted
|
|
76
|
-
$formattedHistory = [];
|
|
77
|
-
foreach ((array) $conversationHistory as $key => $message) {
|
|
78
|
-
if (is_array($message) && isset($message['role'], $message['content']) && Validator::string($message['content'])) {
|
|
79
|
-
$formattedHistory[] = $message;
|
|
80
|
-
} else {
|
|
81
|
-
// If the message is a string, assume it's a user message without a role
|
|
82
|
-
$formattedHistory[] = ['role' => 'user', 'content' => (string) $message];
|
|
83
|
-
}
|
|
84
|
-
}
|
|
90
|
+
// Optional: Convert emojis or special patterns in the message
|
|
91
|
+
$userMessage = Validator::emojis($userMessage);
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
// Format the conversation history
|
|
94
|
+
$formattedHistory = $this->formatConversationHistory($conversationHistory);
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
// Add the new user message
|
|
97
|
+
$formattedHistory[] = ['role' => 'user', 'content' => $userMessage];
|
|
98
|
+
|
|
99
|
+
// Check cache first
|
|
100
|
+
$cacheKey = md5(serialize($formattedHistory));
|
|
101
|
+
if (isset($this->cache[$cacheKey])) {
|
|
102
|
+
return $this->cache[$cacheKey];
|
|
103
|
+
}
|
|
91
104
|
|
|
105
|
+
// Determine the appropriate model to use
|
|
106
|
+
$model = $this->determineModel($formattedHistory);
|
|
107
|
+
|
|
108
|
+
try {
|
|
92
109
|
// Sending a POST request to the AI API
|
|
93
110
|
$response = $this->client->request('POST', $this->apiUrl, [
|
|
94
111
|
'headers' => [
|
|
@@ -106,10 +123,18 @@ class ChatGPTClient
|
|
|
106
123
|
$responseBody = $response->getBody();
|
|
107
124
|
$responseContent = json_decode($responseBody, true);
|
|
108
125
|
|
|
109
|
-
//
|
|
110
|
-
|
|
126
|
+
// Check if response is in expected format
|
|
127
|
+
if (isset($responseContent['choices'][0]['message']['content'])) {
|
|
128
|
+
$aiMessage = $responseContent['choices'][0]['message']['content'];
|
|
129
|
+
// Cache the result
|
|
130
|
+
$this->cache[$cacheKey] = $aiMessage;
|
|
131
|
+
return $aiMessage;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw new \RuntimeException('Unexpected API response format.');
|
|
111
135
|
} catch (RequestException $e) {
|
|
112
|
-
|
|
136
|
+
// Log error here if you have a logger
|
|
137
|
+
throw new \RuntimeException("API request failed: " . $e->getMessage(), 0, $e);
|
|
113
138
|
}
|
|
114
139
|
}
|
|
115
140
|
|
|
@@ -119,7 +144,7 @@ class ChatGPTClient
|
|
|
119
144
|
* @param string $gptResponse The raw response from GPT.
|
|
120
145
|
* @return string The formatted HTML.
|
|
121
146
|
*/
|
|
122
|
-
public function formatGPTResponseToHTML($gptResponse)
|
|
147
|
+
public function formatGPTResponseToHTML(string $gptResponse): string
|
|
123
148
|
{
|
|
124
149
|
try {
|
|
125
150
|
// Decode all HTML entities including numeric ones
|
|
@@ -140,12 +165,10 @@ class ChatGPTClient
|
|
|
140
165
|
}, $gptResponse);
|
|
141
166
|
|
|
142
167
|
// Convert bold text (e.g., **text** or __text__ -> <strong>text</strong>)
|
|
143
|
-
$gptResponse = preg_replace('
|
|
144
|
-
$gptResponse = preg_replace('/__(.*?)__/s', '<strong>$1</strong>', $gptResponse);
|
|
168
|
+
$gptResponse = preg_replace('/(\*\*|__)(.*?)\1/', '<strong>$2</strong>', $gptResponse);
|
|
145
169
|
|
|
146
170
|
// Convert italic text (e.g., *text* or _text_ -> <em>text</em>)
|
|
147
|
-
$gptResponse = preg_replace('/(
|
|
148
|
-
$gptResponse = preg_replace('/(?<!_)_(?!_)(.*?)_(?!_)/s', '<em>$1</em>', $gptResponse);
|
|
171
|
+
$gptResponse = preg_replace('/(\*|_)(.*?)\1/', '<em>$2</em>', $gptResponse);
|
|
149
172
|
|
|
150
173
|
// Convert strikethrough text (e.g., ~~text~~ -> <del>text</del>)
|
|
151
174
|
$gptResponse = preg_replace('/~~(.*?)~~/s', '<del>$1</del>', $gptResponse);
|
|
@@ -16,16 +16,25 @@ class Auth
|
|
|
16
16
|
public const ROLE_NAME = '';
|
|
17
17
|
public const PAYLOAD = 'payload_2183A';
|
|
18
18
|
public const COOKIE_NAME = 'pphp_aut_token_D36E5';
|
|
19
|
-
private const PPHPAUTH = 'pphpauth';
|
|
20
19
|
|
|
20
|
+
private static ?Auth $instance = null;
|
|
21
|
+
private const PPHPAUTH = 'pphpauth';
|
|
21
22
|
private $secretKey;
|
|
22
23
|
private $defaultTokenValidity = '1h'; // Default to 1 hour
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
private function __construct()
|
|
25
26
|
{
|
|
26
27
|
$this->secretKey = $_ENV['AUTH_SECRET'];
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
public static function getInstance(): Auth
|
|
31
|
+
{
|
|
32
|
+
if (self::$instance === null) {
|
|
33
|
+
self::$instance = new self();
|
|
34
|
+
}
|
|
35
|
+
return self::$instance;
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
/**
|
|
30
39
|
* Authenticates a user and generates a JWT (JSON Web Token) based on the specified user data
|
|
31
40
|
* and token validity duration. The method first checks if the secret key is set, calculates
|
|
@@ -35,7 +44,7 @@ class Auth
|
|
|
35
44
|
* @param mixed $data User data which can be a simple string or an instance of AuthRole.
|
|
36
45
|
* If an instance of AuthRole is provided, its `value` property will be used as the role in the token.
|
|
37
46
|
* @param string|null $tokenValidity Optional parameter specifying the duration the token is valid for (e.g., '10m', '1h').
|
|
38
|
-
* If null, the default validity period set in the class property is used.
|
|
47
|
+
* If null, the default validity period set in the class property is used, which is 1 hour.
|
|
39
48
|
* The format should be a number followed by a time unit ('s' for seconds, 'm' for minutes,
|
|
40
49
|
* 'h' for hours, 'd' for days), and this is parsed to calculate the exact expiration time.
|
|
41
50
|
*
|
|
@@ -91,7 +100,19 @@ class Auth
|
|
|
91
100
|
*/
|
|
92
101
|
public function isAuthenticated(): bool
|
|
93
102
|
{
|
|
94
|
-
|
|
103
|
+
if (!isset($_COOKIE[self::COOKIE_NAME])) {
|
|
104
|
+
unset($_SESSION[self::PAYLOAD]);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
$jwt = $_COOKIE[self::COOKIE_NAME];
|
|
109
|
+
|
|
110
|
+
$verifyToken = $this->verifyToken($jwt);
|
|
111
|
+
if ($verifyToken === false) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return true;
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
private function calculateExpirationTime(string $duration): int
|
|
@@ -136,7 +157,7 @@ class Auth
|
|
|
136
157
|
{
|
|
137
158
|
try {
|
|
138
159
|
return JWT::decode($jwt, new Key($this->secretKey, 'HS256'));
|
|
139
|
-
} catch (\Exception
|
|
160
|
+
} catch (\Exception) {
|
|
140
161
|
throw new \InvalidArgumentException("Invalid token.");
|
|
141
162
|
}
|
|
142
163
|
}
|
|
@@ -227,7 +248,8 @@ class Auth
|
|
|
227
248
|
public function getPayload()
|
|
228
249
|
{
|
|
229
250
|
if (isset($_SESSION[self::PAYLOAD])) {
|
|
230
|
-
|
|
251
|
+
$value = $_SESSION[self::PAYLOAD][self::PAYLOAD_NAME];
|
|
252
|
+
return is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
|
|
231
253
|
}
|
|
232
254
|
|
|
233
255
|
return null;
|
|
@@ -278,7 +300,7 @@ class Auth
|
|
|
278
300
|
* @param mixed ...$providers An array of provider objects such as GoogleProvider or GithubProvider.
|
|
279
301
|
*
|
|
280
302
|
* Example:
|
|
281
|
-
* $auth =
|
|
303
|
+
* $auth = Auth::getInstance();
|
|
282
304
|
* $auth->authProviders(new GoogleProvider('client_id', 'client_secret', 'redirect_uri'));
|
|
283
305
|
*/
|
|
284
306
|
public function authProviders(...$providers)
|
|
@@ -461,8 +483,7 @@ class GoogleProvider
|
|
|
461
483
|
public string $clientSecret,
|
|
462
484
|
public string $redirectUri,
|
|
463
485
|
public string $maxAge = '30d'
|
|
464
|
-
) {
|
|
465
|
-
}
|
|
486
|
+
) {}
|
|
466
487
|
}
|
|
467
488
|
|
|
468
489
|
class GithubProvider
|
|
@@ -471,6 +492,5 @@ class GithubProvider
|
|
|
471
492
|
public string $clientId,
|
|
472
493
|
public string $clientSecret,
|
|
473
494
|
public string $maxAge = '30d'
|
|
474
|
-
) {
|
|
475
|
-
}
|
|
495
|
+
) {}
|
|
476
496
|
}
|
|
@@ -13,26 +13,17 @@ enum AuthRole: string
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
class AuthConfig
|
|
16
|
+
final class AuthConfig
|
|
17
17
|
{
|
|
18
18
|
public const ROLE_IDENTIFIER = 'role';
|
|
19
19
|
public const IS_ROLE_BASE = false;
|
|
20
20
|
public const IS_TOKEN_AUTO_REFRESH = false;
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* An array listing the public routes that do not require authentication.
|
|
24
|
-
* Routes should be listed as string paths.
|
|
25
|
-
* By default, all routes are public.
|
|
26
|
-
* Example: public static $publicRoutes = ['/']; // This means the home page and others are public and do not require authentication.
|
|
27
|
-
* NOTE: This $publicRoutes = ['/']; is the default setting and does not need to be modified.
|
|
28
|
-
*/
|
|
29
|
-
public static $publicRoutes = ['/'];
|
|
30
|
-
|
|
31
22
|
/**
|
|
32
23
|
* An array of private routes that are accessible to all authenticated users
|
|
33
24
|
* without specific role-based access control. Routes should be listed as string paths.
|
|
34
25
|
* Example: public static $privateRoutes = ['/']; // This makes the home page private
|
|
35
|
-
* Example: public static $privateRoutes = ['profile', 'dashboard/settings']; // These routes are private
|
|
26
|
+
* Example: public static $privateRoutes = ['/profile', '/dashboard/settings']; // These routes are private
|
|
36
27
|
*/
|
|
37
28
|
public static $privateRoutes = [];
|
|
38
29
|
|
|
@@ -5,7 +5,7 @@ namespace Lib\Middleware;
|
|
|
5
5
|
use Lib\Auth\Auth;
|
|
6
6
|
use Lib\Auth\AuthConfig;
|
|
7
7
|
|
|
8
|
-
class AuthMiddleware
|
|
8
|
+
final class AuthMiddleware
|
|
9
9
|
{
|
|
10
10
|
public static function handle($requestUri)
|
|
11
11
|
{
|
|
@@ -17,13 +17,11 @@ class AuthMiddleware
|
|
|
17
17
|
// Check if the user is authorized to access the route or redirect to login
|
|
18
18
|
if (!self::isAuthorized()) {
|
|
19
19
|
redirect('/auth/login');
|
|
20
|
-
exit;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
// Check if the user has the required role to access the route or redirect to denied
|
|
24
23
|
if (AuthConfig::IS_ROLE_BASE && !self::hasRequiredRole($requestUri)) {
|
|
25
24
|
redirect('/denied');
|
|
26
|
-
exit;
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
27
|
|
|
@@ -39,7 +37,7 @@ class AuthMiddleware
|
|
|
39
37
|
|
|
40
38
|
protected static function isAuthorized(): bool
|
|
41
39
|
{
|
|
42
|
-
$auth =
|
|
40
|
+
$auth = Auth::getInstance();
|
|
43
41
|
$cookieName = Auth::COOKIE_NAME;
|
|
44
42
|
if (!isset($_COOKIE[$cookieName])) {
|
|
45
43
|
unset($_SESSION[Auth::PAYLOAD]);
|
|
@@ -68,7 +66,7 @@ class AuthMiddleware
|
|
|
68
66
|
|
|
69
67
|
protected static function hasRequiredRole($requestUri): bool
|
|
70
68
|
{
|
|
71
|
-
$auth =
|
|
69
|
+
$auth = Auth::getInstance();
|
|
72
70
|
$roleBasedRoutes = AuthConfig::$roleBasedRoutes ?? [];
|
|
73
71
|
foreach ($roleBasedRoutes as $pattern => $data) {
|
|
74
72
|
if (self::getUriRegex($pattern, $requestUri)) {
|
|
@@ -7,14 +7,12 @@ namespace Lib;
|
|
|
7
7
|
*/
|
|
8
8
|
class StateManager
|
|
9
9
|
{
|
|
10
|
+
private static ?StateManager $instance = null;
|
|
10
11
|
private const APP_STATE = 'app_state_F989A';
|
|
11
12
|
private array $state = [];
|
|
12
13
|
private array $listeners = [];
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
* Initializes a new instance of the StateManager class.
|
|
16
|
-
*/
|
|
17
|
-
public function __construct()
|
|
15
|
+
private function __construct()
|
|
18
16
|
{
|
|
19
17
|
global $isWire;
|
|
20
18
|
|
|
@@ -25,6 +23,14 @@ class StateManager
|
|
|
25
23
|
}
|
|
26
24
|
}
|
|
27
25
|
|
|
26
|
+
public static function getInstance(): StateManager
|
|
27
|
+
{
|
|
28
|
+
if (self::$instance === null) {
|
|
29
|
+
self::$instance = new self();
|
|
30
|
+
}
|
|
31
|
+
return self::$instance;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
/**
|
|
29
35
|
* Gets the state value for the specified key.
|
|
30
36
|
*
|
|
@@ -71,7 +77,7 @@ class StateManager
|
|
|
71
77
|
{
|
|
72
78
|
$this->listeners[] = $listener;
|
|
73
79
|
$listener($this->state);
|
|
74
|
-
return fn
|
|
80
|
+
return fn() => $this->listeners = array_filter($this->listeners, fn($l) => $l !== $listener);
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
/**
|
|
@@ -5,7 +5,7 @@ namespace Lib;
|
|
|
5
5
|
use HTMLPurifier;
|
|
6
6
|
use HTMLPurifier_Config;
|
|
7
7
|
|
|
8
|
-
class Validator
|
|
8
|
+
final class Validator
|
|
9
9
|
{
|
|
10
10
|
// String Validation
|
|
11
11
|
|
|
@@ -351,4 +351,226 @@ class Validator
|
|
|
351
351
|
|
|
352
352
|
return strtr($content, $emojiMap);
|
|
353
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Validate a value against a set of rules.
|
|
357
|
+
*
|
|
358
|
+
* @param mixed $value The value to validate.
|
|
359
|
+
* @param string $rules A pipe-separated string of rules (e.g., 'required|min:2|max:50').
|
|
360
|
+
* @param mixed $confirmationValue The value to confirm against, if applicable.
|
|
361
|
+
* @return bool|string|null True if validation passes, string with error message if fails, or null for optional field.
|
|
362
|
+
*/
|
|
363
|
+
public static function withRules($value, string $rules, $confirmationValue = null)
|
|
364
|
+
{
|
|
365
|
+
$rulesArray = explode('|', $rules);
|
|
366
|
+
foreach ($rulesArray as $rule) {
|
|
367
|
+
// Handle parameters in rules, e.g., 'min:10'
|
|
368
|
+
if (strpos($rule, ':') !== false) {
|
|
369
|
+
[$ruleName, $parameter] = explode(':', $rule);
|
|
370
|
+
$result = self::applyRule($ruleName, $parameter, $value, $confirmationValue);
|
|
371
|
+
} else {
|
|
372
|
+
$result = self::applyRule($rule, null, $value, $confirmationValue);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// If a validation rule fails, return the error message
|
|
376
|
+
if ($result !== true) {
|
|
377
|
+
return $result;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Apply an individual rule to a value.
|
|
385
|
+
*
|
|
386
|
+
* @param string $rule The rule to apply.
|
|
387
|
+
* @param mixed $parameter The parameter for the rule, if applicable.
|
|
388
|
+
* @param mixed $value The value to validate.
|
|
389
|
+
* @return bool|string True if the rule passes, or a string with an error message if it fails.
|
|
390
|
+
*/
|
|
391
|
+
private static function applyRule(string $rule, $parameter, $value, $confirmationValue = null)
|
|
392
|
+
{
|
|
393
|
+
switch ($rule) {
|
|
394
|
+
case 'required':
|
|
395
|
+
if (empty($value) && $value !== '0') {
|
|
396
|
+
return "This field is required.";
|
|
397
|
+
} else {
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
break;
|
|
401
|
+
case 'min':
|
|
402
|
+
if (strlen($value) < (int)$parameter) {
|
|
403
|
+
return "This field must be at least $parameter characters long.";
|
|
404
|
+
} else {
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
case 'max':
|
|
409
|
+
if (strlen($value) > (int)$parameter) {
|
|
410
|
+
return "This field must not exceed $parameter characters.";
|
|
411
|
+
} else {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
break;
|
|
415
|
+
case 'startsWith':
|
|
416
|
+
if (strpos($value, $parameter) !== 0) {
|
|
417
|
+
return "This field must start with $parameter.";
|
|
418
|
+
} else {
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
422
|
+
case 'endsWith':
|
|
423
|
+
if (substr($value, -strlen($parameter)) !== $parameter) {
|
|
424
|
+
return "This field must end with $parameter.";
|
|
425
|
+
} else {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
429
|
+
case 'confirmed':
|
|
430
|
+
if ($confirmationValue !== $value) {
|
|
431
|
+
return "The $rule confirmation does not match.";
|
|
432
|
+
} else {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
break;
|
|
436
|
+
case 'email':
|
|
437
|
+
return self::email($value) ? true : "This field must be a valid email address.";
|
|
438
|
+
case 'url':
|
|
439
|
+
return self::url($value) ? true : "This field must be a valid URL.";
|
|
440
|
+
case 'ip':
|
|
441
|
+
return self::ip($value) ? true : "This field must be a valid IP address.";
|
|
442
|
+
case 'uuid':
|
|
443
|
+
return self::uuid($value) ? true : "This field must be a valid UUID.";
|
|
444
|
+
case 'cuid':
|
|
445
|
+
return self::cuid($value) ? true : "This field must be a valid CUID.";
|
|
446
|
+
case 'int':
|
|
447
|
+
return self::int($value) !== null ? true : "This field must be an integer.";
|
|
448
|
+
case 'float':
|
|
449
|
+
return self::float($value) !== null ? true : "This field must be a float.";
|
|
450
|
+
case 'boolean':
|
|
451
|
+
return self::boolean($value) !== null ? true : "This field must be a boolean.";
|
|
452
|
+
case 'in':
|
|
453
|
+
if (!in_array($value, explode(',', $parameter), true)) {
|
|
454
|
+
return "The selected value is invalid.";
|
|
455
|
+
} else {
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
break;
|
|
459
|
+
case 'notIn':
|
|
460
|
+
if (in_array($value, explode(',', $parameter), true)) {
|
|
461
|
+
return "The selected value is invalid.";
|
|
462
|
+
} else {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
case 'size':
|
|
467
|
+
if (strlen($value) !== (int)$parameter) {
|
|
468
|
+
return "This field must be exactly $parameter characters long.";
|
|
469
|
+
} else {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
case 'between':
|
|
474
|
+
[$min, $max] = explode(',', $parameter);
|
|
475
|
+
if (strlen($value) < (int)$min || strlen($value) > (int)$max) {
|
|
476
|
+
return "This field must be between $min and $max characters long.";
|
|
477
|
+
} else {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
case 'date':
|
|
482
|
+
return self::date($value, $parameter ?: 'Y-m-d') ? true : "This field must be a valid date.";
|
|
483
|
+
case 'dateFormat':
|
|
484
|
+
if (!\DateTime::createFromFormat($parameter, $value)) {
|
|
485
|
+
return "This field must match the format $parameter.";
|
|
486
|
+
} else {
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
break;
|
|
490
|
+
case 'before':
|
|
491
|
+
if (strtotime($value) >= strtotime($parameter)) {
|
|
492
|
+
return "This field must be a date before $parameter.";
|
|
493
|
+
} else {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
break;
|
|
497
|
+
case 'after':
|
|
498
|
+
if (strtotime($value) <= strtotime($parameter)) {
|
|
499
|
+
return "This field must be a date after $parameter.";
|
|
500
|
+
} else {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
break;
|
|
504
|
+
case 'json':
|
|
505
|
+
return self::json($value) ? true : "This field must be a valid JSON string.";
|
|
506
|
+
break;
|
|
507
|
+
case 'timezone':
|
|
508
|
+
if (!in_array($value, timezone_identifiers_list())) {
|
|
509
|
+
return "This field must be a valid timezone.";
|
|
510
|
+
} else {
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
break;
|
|
514
|
+
case 'regex':
|
|
515
|
+
if (!preg_match($parameter, $value)) {
|
|
516
|
+
return "This field format is invalid.";
|
|
517
|
+
} else {
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
521
|
+
case 'digits':
|
|
522
|
+
if (!ctype_digit($value) || strlen($value) != $parameter) {
|
|
523
|
+
return "This field must be $parameter digits.";
|
|
524
|
+
} else {
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
break;
|
|
528
|
+
case 'digitsBetween':
|
|
529
|
+
[$min, $max] = explode(',', $parameter);
|
|
530
|
+
if (!ctype_digit($value) || strlen($value) < (int)$min || strlen($value) > (int)$max) {
|
|
531
|
+
return "This field must be between $min and $max digits.";
|
|
532
|
+
} else {
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
break;
|
|
536
|
+
case 'mimes':
|
|
537
|
+
$mimeTypes = explode(',', $parameter);
|
|
538
|
+
if (!self::isMimeTypeAllowed($value, $mimeTypes)) {
|
|
539
|
+
return "The file must be of type: " . implode(', ', $mimeTypes) . ".";
|
|
540
|
+
} else {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
case 'file':
|
|
545
|
+
if (!is_uploaded_file($value)) {
|
|
546
|
+
return "This field must be a valid file.";
|
|
547
|
+
} else {
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
break;
|
|
551
|
+
// Add additional rules as needed...
|
|
552
|
+
default:
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private static function isMimeTypeAllowed($file, array $allowedMimeTypes)
|
|
558
|
+
{
|
|
559
|
+
// Check if the file is a valid uploaded file
|
|
560
|
+
if (!is_uploaded_file($file)) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Get the MIME type of the file using PHP's finfo_file function
|
|
565
|
+
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
566
|
+
$mimeType = finfo_file($finfo, $file);
|
|
567
|
+
finfo_close($finfo);
|
|
568
|
+
|
|
569
|
+
// Check if the MIME type is in the list of allowed MIME types
|
|
570
|
+
if (in_array($mimeType, $allowedMimeTypes, true)) {
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
354
576
|
}
|