├── .env.example
├── .gitignore
├── .htaccess
├── LICENSE
├── README.md
├── app
├── Controllers
│ ├── AuthController.php
│ └── Controller.php
├── CssGenerator.php
├── Framework
│ ├── Database
│ │ └── Database.php
│ ├── Helpers
│ │ └── EnvGenerator.php
│ ├── Migrations
│ │ └── Migrator.php
│ ├── Model
│ │ └── BaseModel.php
│ ├── Schema
│ │ └── Schema.php
│ └── Utilities
│ │ ├── BaseUtility.php
│ │ ├── Encrypt.php
│ │ ├── HttpRequest.php
│ │ ├── JwtUtility.php
│ │ ├── Log.php
│ │ ├── Request.php
│ │ ├── Response.php
│ │ └── Upload.php
├── Middlewares
│ └── Authentication.php
├── Models
│ └── UserModel.php
└── View.php
├── composer.json
├── core
├── Route.php
└── env_loader.php
├── public
├── .htaccess
├── css
│ └── style.css
└── index.php
├── routes
├── api.php
└── web.php
├── server.php
├── star
├── storage
├── private
│ └── .gitignore
└── public
│ └── .gitignore
└── views
├── Home.php
├── Test.php
├── layouts
└── main.php
└── widgets
├── footer.php
└── header.php
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Sparkle
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG="true"
5 | APP_URL="http://localhost:8000"
6 |
7 |
8 | MYSQL_HOST=127.0.0.1:3306
9 | MYSQL_DB=sparkle
10 | MYSQL_USERNAME=root
11 | MYSQL_PASSWORD=
12 |
13 | SERVER_SALT="36bybdfs5238nf84498"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /core/Environment.php
3 | .env
4 | .DS_Store
5 | /storage/private/*
6 | /storage/public/*
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | Options -Indexes
2 |
3 | RewriteEngine On
4 | RewriteCond %{REQUEST_URI} !/css
5 | RewriteCond %{REQUEST_URI} !/uploads
6 | RewriteCond %{REQUEST_URI} !/img
7 | RewriteCond %{REQUEST_URI} !/svg
8 | RewriteCond %{REQUEST_URI} !/lofty-admin
9 | RewriteCond %{REQUEST_URI} !/ads.txt
10 | RewriteCond %{REQUEST_URI} !/favicon.ico
11 | RewriteCond %{REQUEST_URI} !/icon.png
12 | RewriteRule ^([^/]+)/? index.php?url=$1 [L,QSA]
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Gabriel Ikuejawa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sparkle
2 |
3 | ## Introduction
4 |
5 | Sparkle is a free to use open source php MVC framework for building web applications. It's very simple to work with and has a very low learning curve.
6 |
7 | Getting started with sparkle is very easy. All you need to do is have `php version >= 8` and `composer` installed on you computer.
8 |
9 | >composer is a package manager for php
10 |
11 | Then in the root directory of the project, run this command:
12 |
13 | ```bash
14 | composer create-project sparkle/sparkle .
15 | ```
16 |
17 | Alternatively you can create a new sparkle project in a new directory:
18 |
19 | ```bash
20 | composer create-project sparkle/sparkle my-project
21 | ```
22 |
23 | To start the server, in the root directory of the project, run this command:
24 | ```bash
25 | php star light
26 | ```
27 | This would start the php dev server on port 8000 and load the environment variables in the `.env` file if you have one.
28 |
29 |
30 | In production, you can load the environment variables using this command:
31 | ```bash
32 | php star configure
33 | ```
34 |
35 | Sparkle is built to be light weight and reliable so it doesn't come with any third party packages but you can extend sparkle by installing third party packages via composer.
36 |
37 |
38 | ## Views
39 | Sparkle was designed for REST api development but also supports basic view templating.
40 |
41 | To create a new web page, go to the `views` directory and create a new file.
42 | Then in the `routes/web.php`, add a new route like so:
43 |
44 | ```php
45 | Route::get('/some-route', function() {
46 | View::make("ViewName");
47 | });
48 |
49 | ```
50 |
51 | Inject data into your views by adding an associative array of key value pairs as the second argument like so:
52 | ```php
53 | Route::get('/some-route', function() {
54 | View::make("ViewName", ['site_title'=>env('APP_NAME')]);
55 | });
56 | ```
57 |
58 | use the variable in your view file using double curly brackets like so:
59 | ```html
60 |
{{ site_title }}
61 | ```
62 |
63 | >You could aslo use a controller to handle view rendering
64 |
65 | create a controller by going to the `app/Controllers` directory and creating a new file with a class extending the base controller.
66 |
67 | ## Controllers
68 | ```php
69 |
78 | ```
79 |
80 | ## Models
81 | Now lets create a model for our user controller. You can do this by going to the `app/Models` directory and create a file, let's say `UserModel.php`
82 |
83 | now add this snippet:
84 |
85 | ```php
86 | id();
99 | $schema->string('username');
100 | $schema->string('firstname');
101 | $schema->string('password');
102 | $schema->string('email');
103 | $schema->string('lastname');
104 | $schema->create();
105 | }
106 | }
107 |
108 | ?>
109 | ```
110 | >The name of the table in this model is `users` as defined here `protected static $tableName = 'users'`
111 | >Note that timestamps `date_created` and `date_updated` are added by default to entries to each table;
112 | >You do not need to create extra migration files after creating your models just run the migrate command and you're good to go!
113 |
114 | your user model should look like this:
115 |
116 | 
117 |
118 | ## Setting up the database
119 |
120 | Now that we have our model set up, we need to make sure our connection to the database is done.
121 | let's configure our environment variables by copying all the contents of `.env.example.php` in the root directory into a new file in the root directory `.env`
122 |
123 | Now create a new database with your desired credentials. You can create a database using a gui like phpmyadmin which comes with xampp or via the mysql cli.
124 | >Once that is done, make sure you start the mysql server.
125 |
126 |
127 | Now edit the values of the .env files with the database credentials you just created. In this tutorial, we're on local host so the `MYSQL_HOST` is `127.0.0.1:3306`
128 | >3306 is the default mysql port
129 | or database name is `sparkle` and our username is `root` the password is blank but you should use your desired credentials.
130 |
131 | ```
132 | DB_CONNECTION=pdo
133 | MYSQL_HOST=127.0.0.1:3306
134 | MYSQL_DB=sparkle
135 | MYSQL_USERNAME=root
136 | MYSQL_PASSWORD=
137 | ```
138 |
139 | now that we have our credentials set up, reload the application configuration by running this command:
140 | ```bash
141 | php star configure
142 | ```
143 |
144 | Now let's migrate our UserModel by running this command:
145 | ```bash
146 | php star migrate
147 | ```
148 |
149 | A new table with the name users has been created in the database
150 |
151 | you can add new columns to your table if you want to or edit existing columns.
152 | >after changes to your model, you need to run `php star migrate` for your changes to effect
153 |
154 | ## Handling requests
155 |
156 | In this example, we're going to be working with api.
157 | To handle requests, we create methods to our controllers and receive data view the `$request` object parameter like so:
158 | ```php
159 | public function doSomething(Request $request) {
160 | $req_body = $request->body;
161 | $req_query = $request->query;
162 | $req_params = $request->params;
163 | $req_extras = $request->extras;
164 | }
165 | ```
166 | The request object carries the body, query, params and extras properties added to the request.
167 |
168 | Now let's create a method to add users and fetch users. In your UserController, add this snippet:
169 |
170 | ```php
171 | body;
182 |
183 | $firstname = $req_data->firstname;
184 | $lastname = $req_data->lastname;
185 | $password = $req_data->password;
186 | $username = $req_data->username;
187 | $email = $req_data->email;
188 |
189 | $user = new UserModel();
190 |
191 | $user->create([
192 | 'id'=>null,
193 | 'username'=>$username,
194 | 'firstname'=>$firstname,
195 | 'lastname'=>$lastname,
196 | 'email'=>$email,
197 | 'password'=>password_hash($password, PASSWORD_BCRYPT),
198 | ]);
199 |
200 | Response::send(array(
201 | 'message'=>'Users created',
202 | 'status'=>1
203 | ), 400);
204 | }
205 |
206 | public static function getUsers(Request $request) {
207 | $user = new UserModel();
208 |
209 | $all_users = $user->all();
210 |
211 | Response::send(array(
212 | 'message'=>'Users fetched successfully',
213 | 'status'=>1,
214 | 'data'=> $all_users
215 | ), 200);
216 | }
217 | }
218 | ```
219 |
220 | Your controller should look like this:
221 |
222 | 
223 |
224 | The above snippets creates a new user by creating a new instance of the UserModel and calling the create method like so:
225 |
226 | ```php
227 | $user = new UserModel();
228 |
229 | $user->create([
230 | 'id'=>null,
231 | 'username'=>$username,
232 | 'firstname'=>$firstname,
233 | 'lastname'=>$lastname,
234 | 'email'=>$email,
235 | 'password'=>password_hash($password, PASSWORD_BCRYPT),
236 | ]);
237 | ```
238 |
239 | >kindly note that the values in the request body are gotten from the request made to this endpoint and you should validate your requests.
240 |
241 | Now lets bind an endpoint to this route. In the `routes/api.php`, add this:
242 | ```php
243 | Route::post('/users/add', [App\Controllers\UserController::class, 'addUser']);
244 | Route::get('/users/fetch', [App\Controllers\UserController::class, 'getUsers']);
245 | ```
246 |
247 | you can also bind your routes like this:
248 |
249 | ```php
250 | use App\Controllers\UserController;
251 |
252 | Route::post('/users/get', function($request) {
253 | $userController = new UserController();
254 | UserController->getUsers($request);
255 | });
256 |
257 | ```
258 | > note that we imported the UserController using this `use App\Controllers\UserController;`.
259 |
260 |
261 | We also fetch all users in the table using this snippet:
262 |
263 | ```php
264 | $user = new UserModel();
265 | $all_users = $user->all();
266 | ```
267 |
268 |
269 | Other methods for working with models are:
270 | ```php
271 | Model::insertMany()
272 |
273 | $model->findById()
274 |
275 | $model->where('balance', 100)->get()
276 | $model->where('email', "examle@gmail.com")->first()
277 |
278 | ```
279 |
280 | To update your entries in the table, we do it like this:
281 |
282 | ```php
283 | $model = new Model();
284 | $model->where('email', "examle@gmail.com")->and("first_name", "james")->update("lastname", "felix");
285 | ```
286 | This evaluates to `UPDATE model SET lastname=felix WHERE email="example@gmail.com AND first_name = "james"`
287 |
288 |
289 | Request parameters are like variables and can be added to endpoints like so:
290 |
291 | ```php
292 | Route::post('/profile/fetch/:id', [App\Controllers\Authentication::class, 'getProfile']);
293 | ```
294 |
295 | Where `:id` is a request parameter and can be accessed via the request object like so:
296 | ```php
297 | $request->params->id
298 | ```
299 |
300 | ## Middlewares
301 | Middlewares are found in the `app/Middlewares` directory and you can create your middlewares there.
302 | Sparkle comes with an Authentication middleware for checking generated tokens generated via the `AuthController`;
303 | Middlewares are basically methods of classes that intercept the request before getting to where the request is handled:
304 |
305 | this is an example:
306 | ```php
307 | Route::get('/v1/profile', [App\Middlewares\Authentication::class, 'check'], [App\Controllers\AuthController::class, 'getProfile']);
308 | ```
309 |
310 | `[App\Middlewares\Authentication::class, 'check']` is the middleware that runs before the method that handles the request
311 |
312 | The default Authentication middleware validates the token set in the header of the incoming request and sets the user_id property to the request like so:
313 |
314 | ```php
315 | $request->setExtras(['user_id'=> 45]);
316 | ```
317 |
318 | >note that middlewares must return the request object for the request to continue to the next handler
319 |
320 | This is an image of Authentication middleware:
321 |
322 |
323 | 
324 |
325 |
326 | ## Api request
327 |
328 | Sparkle comes with a utility class for sending and listening to api requests.
329 | Create a request by importing the HttpRequest utility to your controller like so:
330 |
331 | ```php
332 | use App\Framework\Utilities\HttpRequest;
333 |
334 | class MyController extends Controller {
335 | public function doSomething($request) {
336 | $res = HttpRequest::post("api.example.com/post", [
337 | "foo"=>"bar"
338 | ]);
339 |
340 | $res->status_code; // gets http response code
341 | $res->data; // gets the data returned
342 | }
343 | }
344 |
345 | ```
346 |
347 | You can also add headers to your requests by chaining the withHeaders method passing an associative array as an arguments like so:
348 |
349 | ```php
350 | use App\Framework\Utilities\HttpRequest;
351 |
352 | class MyController extends Controller {
353 | public function doSomething($request) {
354 | $headers = [
355 | 'Content-Type'=> 'application/json',
356 | 'Accept'=> 'application/json'
357 | ];
358 |
359 | $res = HttpRequest::withHeaders($headers)::post("api.example.com/post", [
360 | "foo"=>"bar"
361 | ]);
362 |
363 | $res->status_code; // gets http response code
364 | $res->data; // gets the data returned
365 | }
366 | }
367 |
368 | ```
369 |
370 | >do not pass data to get requests
371 |
372 | HttpRequest has the following methods for sending requests:
373 |
374 | ```php
375 | HttpRequest::patch("api.example.com/post", []);
376 | HttpRequest::put("api.example.com/post", []);
377 | HttpRequest::post("api.example.com/post", []);
378 | HttpRequest::delete("api.example.com/post", []);
379 | HttpRequest::get("api.example.com/post");
380 |
381 | ```
382 |
383 |
384 | ## Author's note
385 | More features are been added to the framework. you can support me by buying me a coffee
386 |
387 | [](https://www.buymeacoffee.com/loftytech)
388 |
389 | If you notice any bugs or you have a feature you want us to include, kindly reachout to me on [twitter](https://twitter.com/loftycodes)
390 |
391 | thanks!
--------------------------------------------------------------------------------
/app/Controllers/AuthController.php:
--------------------------------------------------------------------------------
1 | body;
13 |
14 | $firstname = isset($req_data->firstname) ? trim($req_data->firstname) : "";
15 | $lastname = isset($req_data->lastname) ? trim($req_data->lastname) : "";
16 | $password = isset($req_data->password) ? $req_data->password : "";
17 | $username = isset($req_data->username) ? trim(strtolower($req_data->username)) : "";
18 | $email = isset($req_data->email) ? strtolower(trim($req_data->email)) : "";
19 |
20 |
21 | if (strlen($firstname) >= 3 && strlen($firstname) < 32) {
22 | if (strlen($lastname) >= 3 && strlen($lastname) < 32) {
23 | if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
24 | $user = new UserModel();
25 | $ests = $user->where('email', $email)->get();
26 | if (!$user->where('email', $email)->get()) {
27 | $user->create([
28 | 'id'=>null,
29 | 'username'=>$username,
30 | 'firstname'=>$firstname,
31 | 'lastname'=>$lastname,
32 | 'email'=>$email,
33 | 'password'=>password_hash($password, PASSWORD_BCRYPT),
34 | ]);
35 |
36 | $newUser = $user->where('email', $email)->first();
37 |
38 | $user_id = $newUser->id;
39 | $user_data = array('email'=>$email, 'user_id'=>$user_id, 'exp'=>(time() + 86400));
40 | $auth_token = JwtUtility::get_token($user_data);
41 | Response::send(array(
42 | 'message'=>'user registered successfully',
43 | 'status'=>1,
44 | 'data'=> [
45 | 'token'=>$auth_token
46 | ]
47 | ), 200);
48 | } else {
49 | Response::send(array(
50 | 'message'=>'Email already registered!',
51 | 'status'=>0
52 | ), 400);
53 | }
54 | } else {
55 | Response::send(array(
56 | 'message'=>'Invalid email!',
57 | 'status'=>0
58 | ), 400);
59 | }
60 | } else {
61 | Response::send(array(
62 | 'message'=>'Invalid lastname character length',
63 | 'status'=>0
64 | ), 400);
65 | }
66 | }else {
67 | Response::send(array(
68 | 'message'=>'Invalid firstname character length',
69 | 'status'=>0
70 | ), 400);
71 | }
72 | }
73 |
74 | public static function login(Request $request) {
75 | $req_data = $request->body;
76 |
77 | $email = isset($req_data->email) ? strtolower($req_data->email) : "";
78 | $password = isset($req_data->password) ? $req_data->password : "";
79 |
80 | $user = new UserModel();
81 | if ($user->where('email', $email)->first()) {
82 | if (password_verify($password, $user->where('email', $email)->first()->password)) {
83 | $user = $user->where('email', $email)->first();
84 | $user_data = array('email'=>$email, 'user_id'=>$user->id, 'exp'=>(time() + 86400));
85 | $auth_token = JwtUtility::get_token($user_data);
86 |
87 | Response::send(array(
88 | 'message'=>'user logged in successfully',
89 | 'status'=>1,
90 | 'data'=> [
91 | 'token'=>$auth_token,
92 | 'firstName'=>$user->firstname,
93 | 'lastName'=>$user->lastname,
94 | 'email'=>$user->email,
95 | ]
96 | ), 200);
97 | } else {
98 | Response::send(array(
99 | 'message'=>'Invalid email or password',
100 | 'status'=>0
101 | ), 400);
102 | }
103 | } else {
104 | Response::send(array(
105 | 'message'=>'Invalid email or password',
106 | 'status'=>0
107 | ), 400);
108 | }
109 | }
110 |
111 |
112 | public static function getProfile(Request $request) {
113 | $req_data = $request->body;
114 |
115 | $user = new UserModel();
116 |
117 | Response::send(array(
118 | 'message'=>'profile fetched successfully',
119 | 'status'=>1,
120 | 'data'=> $user->findById($request->extras->user_id)
121 | ), 200);
122 | }
123 | }
--------------------------------------------------------------------------------
/app/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | env('APP_NAME'),
11 | ];
12 | View::useLayout($viewName, $data);
13 | }
14 |
15 | public static function useTemplate($viewName, $params = null) {
16 | $params = (object) $params;
17 |
18 | $data = [
19 | 'site_title'=>env('APP_NAME'),
20 | ];
21 | View::make($viewName, $data);
22 | }
23 |
24 |
25 |
26 | public static function testCreateView($viewName, $params = null) {
27 | $params = (object) $params;
28 |
29 | $data = [
30 | 'site_title'=>env('APP_NAME'),
31 | ];
32 | View::makeTest($viewName, $data);
33 | }
34 | }
35 |
36 | ?>
--------------------------------------------------------------------------------
/app/CssGenerator.php:
--------------------------------------------------------------------------------
1 | scssToNestedArray($scss);
18 | $obj_arr = json_decode($json_string);
19 |
20 | $this->generated_object = $obj_arr;
21 |
22 | $this->nestedArraysTocss($obj_arr);
23 |
24 | $this->generateCss();
25 | }
26 |
27 | private function scssToNestedArray($scss) {
28 | $processedContent = str_replace('"', "'", $scss);
29 |
30 | // convert selectors to objects
31 | $processedContent = preg_replace_callback("/(.*)(\{)/", function ($matches) {
32 | return '"'.trim($matches[1]).'":'.'{';
33 | }, $processedContent);
34 |
35 | // convert css properties to keys
36 | $processedContent = preg_replace_callback("/(.*)(:)(|\s+)(.*)(|\s+)(;)/", function ($matches) {
37 | return '"'.trim($matches[1]).'": "'.trim($matches[4]).'",';
38 | }, $processedContent);
39 |
40 | // cleaning up the json string
41 | $processedContent = preg_replace_callback('/(})(|\s+)(")(.*)/', function ($matches) {
42 | return trim($matches[1]).', "'.trim($matches[4]);
43 | }, $processedContent);
44 |
45 | $processedContent = preg_replace_callback('/(",)(|\s+)(})/', function ($matches) {
46 | return '" }';
47 | }, $processedContent);
48 |
49 | return "{".$processedContent."}";
50 | }
51 |
52 | private function nestedArraysTocss($nestedArray, $parent_selector = "") {
53 | foreach ($nestedArray as $key => $value) {
54 |
55 | if (trim($key)[0] == "@") {
56 |
57 | $this->extractMediaQueries($value, $parent_selector, $key);
58 | } else {
59 | if (gettype($value) == "string") {
60 |
61 | $full_selector = $parent_selector;
62 | if ($parent_selector == "") {
63 | $full_selector = $key;
64 | }
65 |
66 | if (trim($key)[0] == ":") {
67 | $full_selector = trim($parent_selector) . trim($key);
68 | }
69 |
70 | if (!array_key_exists($full_selector, $this->compiled_array)) {
71 | $this->compiled_array[$full_selector] = ["$key: $value;"];
72 | } else {
73 | $style_arr = $this->compiled_array[$full_selector];
74 | array_push($style_arr, "$key: $value;");
75 |
76 | $this->compiled_array[$full_selector] = $style_arr;
77 | }
78 | } else {
79 |
80 | foreach ($value as $child_key => $child_value) {
81 | if (gettype($child_value) == "string") {
82 | $full_selector = $parent_selector . " ". $key;
83 |
84 | if ($parent_selector == "") {
85 | $full_selector = $key;
86 | }
87 |
88 | if (trim($key)[0] == ":") {
89 | $full_selector = trim($parent_selector) . trim($key);
90 | }
91 |
92 | if (!array_key_exists($full_selector, $this->compiled_array)) {
93 | $this->compiled_array[$full_selector] = ["$child_key: $child_value;"];
94 | } else {
95 | $style_arr = $this->compiled_array[$full_selector];
96 | array_push($style_arr, "$child_key: $child_value;");
97 |
98 | $this->compiled_array[$full_selector] = $style_arr;
99 | }
100 | } else {
101 |
102 | $trim_key = trim($key);
103 | $trim_child_key = trim($child_key);
104 | $trim_parent_selector = trim($parent_selector);
105 |
106 | $full_selector = $trim_parent_selector . " " . $trim_key . " " . $trim_child_key;
107 |
108 | // if ($parent_selector == "") {
109 | // $full_selector = $trim_key . " " . $trim_child_key;
110 | // }
111 |
112 | $full_child_key = "";
113 |
114 | $child_key_arr = explode(",", $trim_child_key);
115 |
116 | foreach ($child_key_arr as $arr_key => $child_key_value) {
117 | $trimed_value = trim($child_key_value);
118 | $full_child_key = $full_child_key . ($arr_key > 0 ? ", " : "") . $trim_parent_selector . ($trim_key[0] == ":" ? $trim_key : " " . $trim_key) . ($trimed_value[0] == ":" ? $trimed_value : " " . $trimed_value);
119 | }
120 |
121 | $full_selector = $full_child_key;
122 |
123 |
124 | if ($trim_child_key[0] == "@") {
125 | $this->extractMediaQueries($value->$child_key, $trim_parent_selector . ($trim_key[0] == ":" ? $trim_key : " " . $trim_key), $trim_child_key);
126 | } else {
127 | $this->nestedArraysTocss($value->$child_key, $full_selector);
128 | }
129 | }
130 | }
131 | }
132 | }
133 |
134 | }
135 | }
136 |
137 | private function extractMediaQueries($nestedArray, $parent_selector, $scope_selector = "") {
138 | $this->temp_arr = [];
139 |
140 | $this->resolveCss($nestedArray, $parent_selector);
141 |
142 | array_push($this->media_query_arr, [$scope_selector => $this->temp_arr]);
143 | }
144 |
145 |
146 | private function resolveCss($nestedArray, $parent_selector) {
147 | foreach ($nestedArray as $key => $value) {
148 | if (gettype($value) == "string") {
149 | $full_selector = $parent_selector;
150 | if ($parent_selector == "") {
151 | $full_selector = $key;
152 | }
153 |
154 | if (trim($key)[0] == ":") {
155 | $full_selector = trim($parent_selector) . trim($key);
156 | }
157 |
158 | if (!array_key_exists($full_selector, $this->temp_arr)) {
159 | $this->temp_arr[$full_selector] = ["$key: $value;"];
160 | } else {
161 | $style_arr = $this->temp_arr[$full_selector];
162 | array_push($style_arr, "$key: $value;");
163 |
164 | $this->temp_arr[$full_selector] = $style_arr;
165 | }
166 | } else {
167 |
168 | foreach ($value as $child_key => $child_value) {
169 | if (gettype($child_value) == "string") {
170 | $full_selector = $parent_selector . " ". $key;
171 |
172 | if ($parent_selector == "") {
173 | $full_selector = $key;
174 | }
175 |
176 | if (trim($key)[0] == ":") {
177 | $full_selector = trim($parent_selector) . trim($key);
178 | }
179 |
180 | if (!array_key_exists($full_selector, $this->temp_arr)) {
181 | $this->temp_arr[$full_selector] = ["$child_key: $child_value;"];
182 | } else {
183 | $style_arr = $this->temp_arr[$full_selector];
184 | array_push($style_arr, "$child_key: $child_value;");
185 |
186 | $this->temp_arr[$full_selector] = $style_arr;
187 | }
188 | } else {
189 |
190 | $trim_key = trim($key);
191 | $trim_child_key = trim($child_key);
192 | $trim_parent_selector = trim($parent_selector);
193 |
194 | $full_selector = $trim_parent_selector . " " . $trim_key . " " . $trim_child_key;
195 |
196 | // if ($parent_selector == "") {
197 | // $full_selector = $trim_key . " " . $trim_child_key;
198 | // }
199 |
200 | $full_child_key = "";
201 |
202 | $child_key_arr = explode(",", $trim_child_key);
203 |
204 | foreach ($child_key_arr as $arr_key => $child_key_value) {
205 | $trimed_value = trim($child_key_value);
206 | $full_child_key = $full_child_key . ($arr_key > 0 ? ", " : "") . $trim_parent_selector . ($trim_key[0] == ":" ? $trim_key : " " . $trim_key) . ($trimed_value[0] == ":" ? $trimed_value : " " . $trimed_value);
207 | }
208 |
209 | $full_selector = $full_child_key;
210 |
211 | $this->resolveCss($value->$child_key, $full_selector);
212 | }
213 | }
214 | }
215 | }
216 | }
217 |
218 |
219 | private function generateCss() {
220 | foreach ($this->compiled_array as $key => $value) {
221 | $this->compiled_css = $this->compiled_css . $key . " {";
222 | foreach ($value as $property => $style) {
223 | $this->compiled_css = $this->compiled_css . $style;
224 | }
225 | $this->compiled_css = $this->compiled_css . "}";
226 | }
227 |
228 | foreach ($this->media_query_arr as $key => $value) {
229 | foreach ($value as $media_key => $media_queries) {
230 | $this->compiled_media_queries = $this->compiled_media_queries . $media_key . " {";
231 | foreach ($media_queries as $styles_key => $styles_value) {
232 | $this->compiled_media_queries = $this->compiled_media_queries . $styles_key . " {";
233 | foreach ($styles_value as $key => $css_style) {
234 | $this->compiled_media_queries = $this->compiled_media_queries . $css_style;
235 | }
236 | $this->compiled_media_queries = $this->compiled_media_queries . "}";
237 | }
238 | $this->compiled_media_queries = $this->compiled_media_queries . "}";
239 | }
240 | }
241 |
242 | $this->compiled_css = $this->compiled_css . " ". $this->compiled_media_queries;
243 | }
244 |
245 |
246 | }
247 | ?>
--------------------------------------------------------------------------------
/app/Framework/Database/Database.php:
--------------------------------------------------------------------------------
1 | true));
21 | self::$pdoInstance->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
22 | }
23 |
24 | return self::$pdoInstance;
25 | }
26 |
27 | public static function query($query, $params = array(), $returnArray = false) {
28 | // echo $query . "\n\n";
29 | $statement = self::connect()->prepare($query);
30 | $statement->execute($params);
31 |
32 | $queryType = explode(' ', $query)[0];
33 |
34 | if ($queryType == 'SELECT' || $queryType == 'DESCRIBE') {
35 |
36 | $data = $statement->fetchAll($returnArray == false ? \PDO::FETCH_OBJ : \PDO::FETCH_ASSOC);
37 | return $data;
38 |
39 | }
40 | }
41 |
42 | public static function checkTable($table, $params = array()) {
43 | self::$host = env("MYSQL_HOST");
44 | self::$dbName = env("MYSQL_DB");
45 | self::$username = env("MYSQL_USERNAME");
46 | self::$password = env("MYSQL_PASSWORD");
47 |
48 | $query = "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE FROM information_schema.TABLES WHERE TABLE_SCHEMA LIKE '".self::$dbName."' AND TABLE_TYPE LIKE 'BASE TABLE' AND TABLE_NAME = '".$table."'";
49 | $statement = self::connect()->prepare($query);
50 | $statement->execute($params);
51 | $data = $statement->fetchAll(\PDO::FETCH_OBJ);
52 | return $data;
53 | }
54 | }
55 | ?>
--------------------------------------------------------------------------------
/app/Framework/Helpers/EnvGenerator.php:
--------------------------------------------------------------------------------
1 | $value) {
25 | putenv("$key=$value");
26 | }
27 | }
28 | }
29 |
30 | public static function generate() {
31 | $envPath = __DIR__ . "/../../../.env";
32 | $handle = fopen($envPath, "r");
33 |
34 | $envArray = [];
35 | $envArrayString = "[\n";
36 | if ($handle) {
37 | while (($line = fgets($handle)) !== false) {
38 | $contents = trim($line);
39 |
40 | if (strlen($contents) >= 3) {
41 | if ($contents[0] != "#") {
42 | $data = explode("=", $contents);
43 | $trimedKey = trim($data[0]);
44 | $raw_value = trim($data[1]);
45 | $value = $raw_value;
46 | if (!empty($value)) {
47 | if ($raw_value[0] == '"') {
48 | $value = substr($value, 1);
49 | }
50 | if ($value[strlen($value) -1] == '"') {
51 | $value = substr($value, 0, -1);
52 | }
53 | }
54 | $envArray = array_merge($envArray, array(
55 | $trimedKey=>$value
56 | ));
57 | }
58 | }
59 | }
60 |
61 | fclose($handle);
62 |
63 | $iteration = 0;
64 | foreach ($envArray as $key => $value) {
65 |
66 | if ($iteration != count($envArray)-1) {
67 | $envArrayString = $envArrayString . " \"".$key."\" => \"".$value."\",\n";
68 | } else {
69 | $envArrayString = $envArrayString . " \"".$key."\" => \"".$value."\"\n];\n";
70 | }
71 |
72 | $iteration++;
73 | }
74 |
75 | $envFileContentPath = __DIR__ . "/../../../core/Environment.php";
76 |
77 | $myfile = fopen($envFileContentPath, "w") or die("Unable to open file!");
78 | fwrite($myfile, "
--------------------------------------------------------------------------------
/app/Framework/Migrations/Migrator.php:
--------------------------------------------------------------------------------
1 | isDot()) {
13 | $raw_file_name = $fileinfo->getFilename();
14 | $file_name = str_replace(".php", "", $raw_file_name);
15 |
16 | array_push($migrationList, [
17 | $file_name,
18 | ["App\Models\\"."$file_name", 'table']
19 | ]);
20 | }
21 | }
22 |
23 | foreach($migrationList as $migration) {
24 | if (is_callable($migration[1])) {
25 | $migration[1]();
26 | // echo "\033[32m ".$migration[0]." migrated successfully \033[0m\n";
27 | }
28 | }
29 |
30 |
31 | }
32 | }
33 |
34 | new Migrator();
35 |
36 | ?>
--------------------------------------------------------------------------------
/app/Framework/Model/BaseModel.php:
--------------------------------------------------------------------------------
1 | $query) {
25 | if (count($query) >= 3) {
26 | if ($key == 0) {
27 | $search = " WHERE ";
28 | } else {
29 | $search = " AND ";
30 | }
31 | $search = $search . $query[0] . $query[1].":".$query[0];
32 | $param = array(
33 | ":".$query[0] => $query[2]
34 | );
35 | $query_params = array_merge($query_params, $param);
36 | }
37 | }
38 |
39 | $query_limit = "";
40 |
41 | if ($limit > 0) {
42 | $query_limit = " LIMIT " . $limit;
43 | } else {
44 | $query_limit = "";
45 | }
46 |
47 | $sql_query = "SELECT * FROM ".static::$tableName." ".$search .$query_limit;
48 | $data = DB::query($sql_query, $query_params);
49 |
50 | return $data;
51 | }
52 |
53 | public static function findById(int | string $id, string $tableId = "id") {
54 | $sql_query = "SELECT * FROM ".static::$tableName." WHERE ".$tableId." = :row_id";
55 | $data = DB::query($sql_query, [":row_id"=> (int) $id]);
56 |
57 | if (count($data) > 0) {
58 | return $data[0];
59 | } else {
60 | return null;
61 | }
62 | }
63 |
64 | public function all() {
65 | $sql_query = "SELECT * FROM ".static::$tableName;
66 | $data = DB::query($sql_query, []);
67 |
68 | return $data;
69 | }
70 |
71 | public static function findOne(array $queries = []) {
72 | $query_params = [];
73 | $search = "";
74 |
75 | foreach ($queries as $key => $query) {
76 | if (count($query) >= 3) {
77 | if ($key == 0) {
78 | $search = " WHERE ";
79 | } else {
80 | $search = " AND ";
81 | }
82 | $search = $search . $query[0] . $query[1].":".$query[0];
83 | $param = array(
84 | ":".$query[0] => $query[2]
85 | );
86 | $query_params = array_merge($query_params, $param);
87 | }
88 | }
89 |
90 | $sql_query = "SELECT * FROM ".static::$tableName." ".$search;
91 | $data = DB::query($sql_query, $query_params);
92 |
93 | if (!$data) {
94 | return false;
95 | }
96 | return $data[0];
97 | }
98 |
99 |
100 | public static function fetch(array $queries = []): self {
101 | $query_params = [];
102 | $search = "";
103 |
104 | foreach ($queries as $key => $query) {
105 | if (count($query) >= 3) {
106 | if ($key == 0) {
107 | $search = " WHERE ";
108 | } else {
109 | $search = " AND ";
110 | }
111 | $search = $search . $query[0] . $query[1].":".$query[0];
112 | $param = array(
113 | ":".$query[0] => $query[2]
114 | );
115 | $query_params = array_merge($query_params, $param);
116 | }
117 | }
118 |
119 | $sql_query = "SELECT * FROM ".static::$tableName." ".$search;
120 |
121 | return new BaseModel($sql_query, $query_params);
122 | }
123 |
124 |
125 | public function where(string $key, string $inputValue, $extra = "none"): self {
126 | $model = new $this;
127 | $model->param_count = $model->param_count + 1;
128 | $query_params = [];
129 | $search = "";
130 |
131 | $value = "";
132 | $operand = "=";
133 |
134 | if ($extra == "none") {
135 | $value = $inputValue;
136 | } else {
137 | $value = $extra;
138 | $operand = $inputValue;
139 | }
140 |
141 | $search = " WHERE ";
142 |
143 | $model->where_sub_query = $search . $key . $operand.":where_".$key;
144 | $param = array(
145 | ":where_".$key => $value
146 | );
147 |
148 | $model->query_params = array_merge($query_params, $param);
149 |
150 | return $model;
151 | }
152 |
153 |
154 | public function and(string $key, string $inputValue, $extra = "none") {
155 | $this->param_count++;
156 | $query_params = [];
157 | $search = "";
158 |
159 | $value = "";
160 | $operand = "=";
161 |
162 | if ($extra == "none") {
163 | $value = $inputValue;
164 | } else {
165 | $value = $extra;
166 | $operand = $inputValue;
167 | }
168 |
169 | $this->where_sub_query = $this->where_sub_query . " AND " .$key . $operand.":where_and_" . $this->param_count . "_" . $key;
170 | $param = array(
171 | ":where_and_" . $this->param_count . "_" . $key => $value
172 | );
173 |
174 | $this->query_params = array_merge($this->query_params, $param);
175 |
176 | return $this;
177 | }
178 |
179 | public function andNot(string $key, string $inputValue, $extra = "none") {
180 | $this->param_count++;
181 | $query_params = [];
182 | $search = "";
183 |
184 | $value = "";
185 | $operand = "<>";
186 |
187 | if ($extra == "none") {
188 | $value = $inputValue;
189 | } else {
190 | $value = $extra;
191 | $operand = $inputValue;
192 | }
193 |
194 | $this->where_sub_query = $this->where_sub_query . " AND " .$key . $operand.":where_andNot_" . $this->param_count . "_" . $key;
195 | $param = array(
196 | ":where_andNot_" . $this->param_count . "_" . $key => $value
197 | );
198 |
199 | $this->query_params = array_merge($this->query_params, $param);
200 |
201 | return $this;
202 | }
203 |
204 | public function increment(string $key, string $inputValue, $extra = "none") {
205 | $query_params = [];
206 | $search = "";
207 |
208 | $value = "";
209 | $operand = "=";
210 |
211 | if ($extra == "none") {
212 | $value = $inputValue;
213 | } else {
214 | $value = $extra;
215 | $operand = $inputValue;
216 | }
217 |
218 | if (strlen($this->update_search) > 0) {
219 | $this->update_search = $this->update_search . ", " . $key . $operand."$key + :where_".$key;
220 | } else {
221 | $this->update_search = $this->update_search . $key . $operand."$key + :where_".$key;
222 | }
223 |
224 | $param = array(
225 | ":where_".$key => $value
226 | );
227 |
228 | $this->query_params = array_merge($this->query_params, $param);
229 |
230 | return $this;
231 | }
232 |
233 | public function decrement(string $key, string $inputValue, $extra = "none") {
234 | $query_params = [];
235 | $search = "";
236 |
237 | $value = "";
238 | $operand = "=";
239 |
240 | if ($extra == "none") {
241 | $value = $inputValue;
242 | } else {
243 | $value = $extra;
244 | $operand = $inputValue;
245 | }
246 |
247 | if (strlen($this->update_search) > 0) {
248 | $this->update_search = $this->update_search . ", " . $key . $operand."$key - :where_".$key;
249 | } else {
250 | $this->update_search = $this->update_search . $key . $operand."$key - :where_".$key;
251 | }
252 |
253 | $param = array(
254 | ":where_".$key => $value
255 | );
256 |
257 | $this->query_params = array_merge($this->query_params, $param);
258 |
259 | return $this;
260 | }
261 |
262 | public function or(string $key, string $inputValue, $extra = "none") {
263 | $this->param_count++;
264 | $query_params = [];
265 | $search = "";
266 |
267 | $value = "";
268 | $operand = "=";
269 |
270 | if ($extra == "none") {
271 | $value = $inputValue;
272 | } else {
273 | $value = $extra;
274 | $operand = $inputValue;
275 | }
276 |
277 | $this->where_sub_query = $this->where_sub_query . " OR " .$key . $operand.":where_or_" . $this->param_count . "_" . $key;
278 | $param = array(
279 | ":where_or_" . $this->param_count . "_" . $key => $value
280 | );
281 |
282 | $this->query_params = array_merge($this->query_params, $param);
283 |
284 | return $this;
285 | }
286 |
287 | protected static function getTableName() {
288 | return static::$tableName;
289 | }
290 |
291 | public function update(array $queries = []) {
292 | $query_params = [];
293 | $search = "";
294 | $iteration = 0;
295 |
296 | foreach ($queries as $key => $query) {
297 | if ($iteration == 0) {
298 | $search = $key . "=".":update_".$key;
299 | } else {
300 | $search = $search . ", " . $key . "=".":update_".$key;
301 | }
302 | $param = array(
303 | ":update_".$key => $query
304 | );
305 | $this->query_params = array_merge($this->query_params, $param);
306 |
307 | $iteration++;
308 | }
309 |
310 | $search = $search . ", date_updated=now()";
311 |
312 | $this->update_search = $this->update_search . $search;
313 |
314 |
315 | $this->query_params = array_merge($this->query_params, $query_params);
316 |
317 | $sql_query = "UPDATE ". static::$tableName . " SET " . $this->update_search . $this->where_sub_query . $this->query_order_by . $this->query_limit . $this->query_offset;
318 |
319 |
320 | // print_r($this->query_params);
321 | // echo $sql_query;
322 | // error_log($sql_query);
323 |
324 | // exit;
325 |
326 |
327 | $data = DB::query( $sql_query, $this->query_params);
328 | return $data;
329 | }
330 |
331 | public function get(string ...$columns) {
332 | $slected_columns = "*";
333 |
334 | if (count($columns) > 0) {
335 | foreach ($columns as $key => $column) {
336 | if ($key == 0) {
337 | $slected_columns = $column;
338 | } else {
339 | $slected_columns = $slected_columns . "," . $column;
340 | }
341 | }
342 | }
343 |
344 | $sql_query = "SELECT ".$slected_columns." FROM ". static::$tableName . $this->where_sub_query . $this->query_order_by . $this->query_limit . $this->query_offset;
345 |
346 | $data = DB::query($sql_query, $this->query_params);
347 |
348 | if (count($data) > 0) {
349 | return $data;
350 | } else {
351 | return [];
352 | }
353 | }
354 |
355 | public function delete() : void {
356 | $sql_query = "DELETE FROM ". static::$tableName . $this->where_sub_query . $this->query_order_by . $this->query_limit . $this->query_offset;
357 | DB::query($sql_query, $this->query_params);
358 | }
359 |
360 | public function remove() : void{
361 | $sql_query = "DELETE FROM ". static::$tableName . $this->where_sub_query . $this->query_order_by . $this->query_limit . $this->query_offset;
362 | DB::query($sql_query, $this->query_params);
363 | }
364 |
365 |
366 | public function count($row_id = "id") {
367 | $sql_query = "SELECT COUNT($row_id) FROM ". static::$tableName . $this->where_sub_query . $this->query_order_by . $this->query_limit . $this->query_offset;
368 |
369 | $data = DB::query($sql_query, $this->query_params);
370 |
371 | return $data[0]->{'COUNT('.$row_id.')'};
372 | }
373 |
374 | public function first(string ...$columns) {
375 | $slected_columns = "*";
376 |
377 | if (count($columns) > 0) {
378 | foreach ($columns as $key => $column) {
379 | if ($key == 0) {
380 | $slected_columns = $column;
381 | } else {
382 | $slected_columns = $slected_columns . "," . $column;
383 | }
384 | }
385 | }
386 |
387 | $sql_query = "SELECT ".$slected_columns." FROM ". static::$tableName . $this->where_sub_query . $this->query_order_by . $this->query_limit . $this->query_offset;
388 |
389 | $data = DB::query($sql_query, $this->query_params);
390 |
391 | if (count($data) > 0) {
392 | return $data[0];
393 | } else {
394 | return null;
395 | }
396 | }
397 |
398 | public function limit(string $limit) {
399 | if ($limit > 0) {
400 | $this->query_limit = " LIMIT " . $limit;
401 | } else {
402 | $this->query_limit = "";
403 | }
404 | return $this;
405 | }
406 |
407 | public function skip(string $offset) {
408 | if ($offset > 0) {
409 | $this->query_offset = " OFFSET " . $offset;
410 | } else {
411 | $this->query_offset = "";
412 | }
413 | return $this;
414 | }
415 |
416 | public function order(string $column = "id", string $order = "DESC") {
417 |
418 | $this->query_order_by = " ORDER BY " . $column . " " .$order . " ";
419 |
420 | return $this;
421 | }
422 |
423 | public static function create(array $data) {
424 | $keys = "";
425 | $columns = "";
426 | $params= [];
427 |
428 | $iteration = 0;
429 |
430 | foreach ($data as $key => $value) {
431 | if ($iteration == 0) {
432 | $keys = $keys .":".$key;
433 | $columns = $columns ."".$key;
434 | } else {
435 | $keys = $keys . ", :". $key;
436 | $columns = $columns .", ".$key;
437 | }
438 |
439 | $params = array_merge($params, [":".$key=>$value]);
440 |
441 | $iteration++;
442 | }
443 |
444 | $columns = $columns . ", date_created, date_updated";
445 | $keys = $keys . ", now(), now()";
446 |
447 | $sql_query = "INSERT INTO ". static::$tableName ." (".$columns.") VALUES (".$keys.")";
448 |
449 | // print_r($params);
450 | // echo $sql_query;
451 | // error_log($sql_query);
452 | // exit;
453 |
454 | $data = DB::query( $sql_query, $params);
455 | }
456 |
457 | public static function insertMany(array $input) {
458 | $totalKeys = "";
459 | $columns = "";
460 | $generatedColumns = false;
461 | $params= [];
462 | $inputIteration = 1;
463 |
464 | foreach ($input as $data) {
465 | $iteration = 0;
466 | $keys = "";
467 |
468 | foreach ($data as $key => $value) {
469 | if ($iteration == 0) {
470 | if (!$generatedColumns) {
471 | $keys = $keys ."(:". $inputIteration ."_".$key;
472 | $columns = $columns ."".$key;
473 | } else {
474 | $keys = $keys.", (:". $inputIteration ."_".$key;
475 | }
476 | } else {
477 | $keys = $keys . ", :". $inputIteration ."_".$key;
478 |
479 | if (!$generatedColumns) {
480 | $columns = $columns .", ".$key;
481 | }
482 | }
483 |
484 |
485 | $params = array_merge($params, [":". $inputIteration ."_".$key=>$value]);
486 |
487 | $iteration++;
488 | }
489 |
490 | $keys = $keys . ", now(), now())";
491 |
492 | $inputIteration++;
493 |
494 | $totalKeys = $totalKeys . " ". $keys;
495 | $generatedColumns = true;
496 | }
497 |
498 |
499 | $columns = $columns . ", date_created, date_updated";
500 |
501 | $sql_query = "INSERT INTO ". static::$tableName ." (".$columns.") VALUES ".$totalKeys."";
502 |
503 | // print_r($params);
504 | // echo $sql_query;
505 | // error_log($sql_query);
506 | // exit;
507 |
508 | $data = DB::query( $sql_query, $params);
509 | }
510 |
511 |
512 | public static function query(string $query, array $params = []) {
513 | $data = DB::query($query, $params);
514 | return $data;
515 | }
516 |
517 | public static function analizeTable($table) {
518 |
519 | $data = DB::query("DESCRIBE " . $table);
520 | // if (count($data) > 0) {
521 | // $this->isTableExist = true;
522 | // }
523 |
524 | return $data;
525 | }
526 |
527 | public function resolveTableName() {
528 | if (static::$tableName != "") {
529 | return static::$tableName;
530 | } else {
531 | $fullClassArr = explode("\\", get_class($this));
532 | $class_name = end($fullClassArr);
533 | $last_class_letter = $class_name[strlen($class_name)-1];
534 | $plural_letter = "s";
535 | if ($last_class_letter == "s" || $last_class_letter == "x" || $last_class_letter == "z") {
536 | $plural_letter = "es";
537 | }
538 | return BaseUtility::toCamelCase($class_name) . $plural_letter;
539 | }
540 | }
541 | }
542 |
543 | ?>
--------------------------------------------------------------------------------
/app/Framework/Schema/Schema.php:
--------------------------------------------------------------------------------
1 | useTimestamps = $timestamps;
30 | $this->tableName = $tableName;
31 | $this->checkTable();
32 | }
33 |
34 | public function disableTimestamps() {
35 | $this->useTimestamps = false;
36 | }
37 |
38 | public function string(string $column, int $length = 64) {
39 | $this->checkTableExists($column);
40 | $this->current_column_index = $this->current_column_index + 1;
41 | array_push($this->newColumnList, [
42 | "Field" => $column,
43 | "Type" => "varchar($length)",
44 | "Null" => "NO",
45 | "Key" => "",
46 | "Default" => null,
47 | "Extra" => "",
48 | ]);
49 | $this->current_column = $column;
50 | $append_comma = "";
51 | if (strlen($this->schema_sql) > 1) {
52 | $append_comma = ",";
53 | }
54 | $this->schema_sql = $this->schema_sql . $append_comma ." `". $column ."` varchar(".$length.") NOT NULL";
55 | $this->current_column = $column;
56 | return $this;
57 | }
58 |
59 |
60 | public function text(string $column) {
61 | $this->checkTableExists($column);
62 | $this->current_column_index = $this->current_column_index + 1;
63 | array_push($this->newColumnList, [
64 | "Field" => $column,
65 | "Type" => "text",
66 | "Null" => "NO",
67 | "Key" => "",
68 | "Default" => null,
69 | "Extra" => "",
70 | ]);
71 | $this->current_column = $column;
72 | $append_comma = "";
73 | if (strlen($this->schema_sql) > 1) {
74 | $append_comma = ",";
75 | }
76 | $this->schema_sql = $this->schema_sql . $append_comma ." `". $column ."` text NOT NULL";
77 | $this->current_column = $column;
78 | return $this;
79 | }
80 |
81 |
82 | public function integer(string $column, int $length = 11) {
83 | $this->checkTableExists($column);
84 | $this->current_column_index = $this->current_column_index + 1;
85 | array_push($this->newColumnList, [
86 | "Field" => $column,
87 | "Type" => "int($length)",
88 | "Null" => "NO",
89 | "Key" => "",
90 | "Default" => null,
91 | "Extra" => "",
92 | ]);
93 | $this->current_column = $column;
94 | $append_comma = "";
95 | if (strlen($this->schema_sql) > 1) {
96 | $append_comma = ",";
97 | }
98 |
99 | $this->schema_sql = $this->schema_sql . $append_comma ." `". $column ."` int(".$length.") NOT NULL";
100 | return $this;
101 | }
102 |
103 |
104 | public function double(string $column, int $length = 11) {
105 | $this->checkTableExists($column);
106 | $this->current_column_index = $this->current_column_index + 1;
107 | array_push($this->newColumnList, [
108 | "Field" => $column,
109 | "Type" => "double",
110 | "Null" => "NO",
111 | "Key" => "",
112 | "Default" => null,
113 | "Extra" => "",
114 | ]);
115 | $this->current_column = $column;
116 | $append_comma = "";
117 | if (strlen($this->schema_sql) > 1) {
118 | $append_comma = ",";
119 | }
120 |
121 | $this->schema_sql = $this->schema_sql . $append_comma ." `". $column ."` double NOT NULL";
122 | return $this;
123 | }
124 |
125 |
126 |
127 | public function datetime(string $column, int $length = 11) {
128 | $this->checkTableExists($column);
129 | $this->current_column_index = $this->current_column_index + 1;
130 | array_push($this->newColumnList, [
131 | "Field" => $column,
132 | "Type" => "datetime",
133 | "Null" => "NO",
134 | "Key" => "",
135 | "Default" => "current_timestamp()",
136 | "Extra" => "",
137 | ]);
138 | $this->current_column = $column;
139 | $append_comma = "";
140 | if (strlen($this->schema_sql) > 1) {
141 | $append_comma = ",";
142 | }
143 |
144 | $this->schema_sql = $this->schema_sql . $append_comma ." `". $column ."` datetime NOT NULL";
145 | return $this;
146 | }
147 |
148 |
149 | public function id(string $column = 'id', int $length = 11) {
150 | $this->checkTableExists($column);
151 | $this->current_column_index = $this->current_column_index + 1;
152 | array_push($this->newColumnList, [
153 | "Field" => $column,
154 | "Type" => "bigint($length)",
155 | "Null" => "NO",
156 | "Key" => "PRI",
157 | "Default" => null,
158 | "Extra" => "auto_increment",
159 | ]);
160 | $this->current_column = $column;
161 | $append_comma = "";
162 | if (strlen($this->schema_sql) > 1) {
163 | $append_comma = ",";
164 | }
165 |
166 | $this->schema_sql = $this->schema_sql . $append_comma ." `". $column ."` bigint(".$length.") NOT NULL AUTO_INCREMENT";
167 | $this->primary_key = ", PRIMARY KEY (".$column.")";
168 | }
169 |
170 | public function autoIncrement() {
171 | $this->newColumnList[$this->current_column_index]["Extra"] = "auto_increment";
172 | $this->schema_sql = $this->schema_sql . " AUTO_INCREMENT";
173 | return $this;
174 | }
175 |
176 | public function primary() {
177 | if ($this->num_primary_keys > 0) {
178 | echo "\e[0;31;40m Aborting migration: Duplicate primary keys detected in ".$this->tableName." table there can only be one auto column and it must be defined as a key \e[0m\n";
179 | exit(1);
180 | }
181 | $this->newColumnList[$this->current_column_index]["Key"] = "PRI";
182 | $this->primary_key = ", PRIMARY KEY (".$this->current_column.")";
183 | $this->num_primary_keys = $this->num_primary_keys+1;
184 | }
185 |
186 | private function checkTableExists ($newColumn) {
187 | if (strlen(trim($newColumn)) < 1) {
188 | echo "\e[0;31;40m Aborting migration: Empty column detected in ".$this->tableName." table. Columns can't be empty \e[0m\n";
189 | exit(1);
190 | }
191 | foreach ( $this->newColumnList as $column) {
192 | if (in_array($newColumn, $column)) {
193 | echo "\e[0;31;40m Aborting migration: Duplicate colunm `".$newColumn."` detected in ".$this->tableName." table. Columns must be unique \e[0m\n";
194 | exit(1);
195 | }
196 | }
197 | }
198 |
199 | private function enableTimestamps() {
200 | $this->checkTableExists("date_created");
201 | $this->checkTableExists("date_updated");
202 | $this->current_column_index = $this->current_column_index + 1;
203 | array_push($this->newColumnList, [
204 | "Field" => "date_created",
205 | "Type" => "datetime",
206 | "Null" => "NO",
207 | "Key" => "",
208 | "Default" => "current_timestamp()",
209 | "Extra" => "",
210 | ]);
211 | array_push($this->newColumnList, [
212 | "Field" => "date_updated",
213 | "Type" => "datetime",
214 | "Null" => "NO",
215 | "Key" => "",
216 | "Default" => "current_timestamp()",
217 | "Extra" => "",
218 | ]);
219 | }
220 |
221 | public function create() {
222 | if (!$this->isTableExist) {
223 |
224 | if ($this->useTimestamps) {
225 | $this->schema_sql = $this->schema_sql . ", `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `date_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP";
226 | }
227 |
228 | // echo "\e[0;35;40m".$this->tableName." doesn't exist \e[0m\n";
229 | $create_table_query = "CREATE TABLE IF NOT EXISTS ". $this->tableName . " (". $this->schema_sql . $this->primary_key.") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
230 | // error_log("\n\n".$create_table_query."\n\n");
231 | DB::query($create_table_query);
232 | echo "\033[32m ".$this->tableName." table created successfully \033[0m\n";
233 | } else {
234 | if ($this->useTimestamps) {
235 | $this->enableTimestamps();
236 | }
237 | $this->compareColumns();
238 | foreach ($this->columnDropList as $column_drop_query) {
239 | // Log::warning($column_drop_query["sql"]);
240 | DB::query($column_drop_query["sql"]);
241 | }
242 | foreach ($this->modificationList as $modification) {
243 | if ($modification["operation_type"] == "ALTER_CHANGE_COLUMN") {
244 | if (in_array($modification["from"], $this->replacedColumnList)) {
245 | $checkedColumn = $this->checkColumnExists($modification["to"]);
246 | if ($checkedColumn->exists) {
247 | if ($modification["from"] != $modification["to"]) {
248 | array_push($this->replacedColumnList, $modification["to"]);
249 | $column = $this->getColumnType($checkedColumn->data->Type);
250 | $this->buildAndRunQuery(queryType: "ALTER_CHANGE_COLUMN", from: $modification["to"], to: $modification["to"]."_ALTERED", type: $column->type, limit: $column->limit, fromType: $modification["from_type"], toType: $modification["to_type"], extras: $checkedColumn->data->Extra);
251 | }
252 | }
253 |
254 | $new_sql = str_replace("CHANGE `".$modification["from"]."`", "CHANGE `".$modification["from"]."_ALTERED`", $modification["sql"]);
255 | // echo "\033[32m ".$new_sql." \033[0m\n";
256 | DB::query($new_sql);
257 | } else {
258 | $checkedColumn = $this->checkColumnExists($modification["to"]);
259 | if ($checkedColumn->exists) {
260 | if ($modification["from"] != $modification["to"]) {
261 | array_push($this->replacedColumnList, $modification["to"]);
262 | $column = $this->getColumnType($checkedColumn->data->Type);
263 |
264 | $this->buildAndRunQuery(queryType: "ALTER_CHANGE_COLUMN", from: $modification["to"], to: $modification["to"]."_ALTERED", type: $column->type, limit: $column->limit, fromType: $modification["from_type"], toType: $modification["to_type"], extras: $checkedColumn->data->Extra);
265 | $this->clearColumn(from: $modification["from"], to: $modification["to"], fromType: $modification["from_type"], toType: $modification["to_type"]);
266 | // Log::neutral($modification["sql"]);
267 | DB::query($modification["sql"]);
268 | } else {
269 | $this->clearColumn(from: $modification["from"], to: $modification["to"], fromType: $modification["from_type"], toType: $modification["to_type"]);
270 | // Log::neutral($modification["sql"]);
271 | DB::query($modification["sql"]);
272 | }
273 | } else {
274 | $this->clearColumn(from: $modification["from"], to: $modification["to"], fromType: $modification["from_type"], toType: $modification["to_type"]);
275 | // Log::neutral($modification["sql"]);
276 | DB::query($modification["sql"]);
277 | }
278 | }
279 |
280 | // Log::neutral($modification["sql"]);
281 | } else {
282 | // Log::neutral($modification["sql"]);
283 | DB::query($modification["sql"]);
284 | }
285 |
286 | }
287 |
288 | if (!$this->skippedMigration) {
289 | echo "\033[32m ".$this->tableName." table migrated successfully \033[0m\n";
290 | }
291 | }
292 | }
293 |
294 | public function checkTable() {
295 | $data = DB::checkTable($this->tableName);
296 | if (count($data) > 0) {
297 | $this->isTableExist = true;
298 | $this->describeTable();
299 | }
300 | return $data;
301 | }
302 |
303 | private function checkColumnExists ($column) {
304 | $query = "DESCRIBE ". $this->tableName;
305 | $data = DB::query($query, [], true);
306 |
307 | $colunm_data = "";
308 |
309 | $column_exists = false;
310 |
311 | foreach ($data as $savedColumnKey => $savedColumn) {
312 | if (in_array($column, $savedColumn)) {
313 | $colunm_data = (object) $data[$savedColumnKey];
314 | $column_exists = true;
315 | break;
316 | }
317 | }
318 | // echo "\n\n\ncolumn ".$column." exist: " . $column_exists."\n\n\n";
319 |
320 | $body = (object) ["exists" => $column_exists, "data"=>$colunm_data];
321 |
322 | return $body;
323 | }
324 |
325 | public function describeTable() {
326 | $query = "DESCRIBE ". $this->tableName;
327 | $data = DB::query($query, [], true);
328 | $table_columns = count($data);
329 | if($this->useTimestamps) {
330 | if ($data[$table_columns-2]["Field"] == "date_created" && $data[$table_columns-1]["Field"] == "date_updated") {
331 | unset($data[$table_columns-1]);
332 | unset($data[$table_columns-2]);
333 | }
334 | }
335 | // Log::success("====== saved columns ======");
336 | // Log::neutral(print_r($data));
337 | $this->savedColumnList = array_values($data);
338 |
339 | }
340 | public function removeTimestampsFromNewColumns () {
341 | $query = "DESCRIBE ". $this->tableName;
342 | $data = $this->newColumnList;
343 | $table_columns = count($data);
344 | if($this->useTimestamps) {
345 | if ($data[$table_columns-2]["Field"] == "date_created" && $data[$table_columns-1]["Field"] == "date_updated") {
346 | unset($data[$table_columns-1]);
347 | unset($data[$table_columns-2]);
348 | }
349 | }
350 | // Log::success("====== new columns ======");
351 | // Log::neutral(print_r($data));
352 | $this->newColumnList = array_values($data);
353 | }
354 |
355 | public function compareColumns() {
356 | $this->removeTimestampsFromNewColumns();
357 | if (count($this->newColumnList) == count($this->savedColumnList)) {
358 | $this->compareAndAlterColumns();
359 | } else {
360 | $this->getNewColumns();
361 | }
362 | }
363 |
364 | public function compareAndAlterColumns() {
365 | // echo "\n\n\n\n\e[0;35;35m new: ". print_r($this->newColumnList) ." in " .$this->tableName." \e[0m\n\n\n\n\n";
366 | // echo "\n\n\n\n\e[0;35;35m saved: ". print_r($this->savedColumnList) ." in " .$this->tableName." \e[0m\n\n\n\n\n";
367 |
368 | $is_columns_match = false;
369 | if (json_encode($this->newColumnList) == json_encode($this->savedColumnList)) {
370 | $is_columns_match = true;
371 | }
372 |
373 | if ($is_columns_match) {
374 | $this->skippedMigration = true;
375 | echo "\e[0;35;35m Skipping ".$this->tableName." table because no changes was found \e[0m\n";
376 | } else {
377 | foreach ($this->newColumnList as $newColumnkey => $column) {
378 | $new_column = $this->getColumnType($column["Type"]);
379 |
380 | $add_primary_key = "";
381 | $auto_increment_sub_query = "";
382 |
383 | $saved_column_to_alter = $this->savedColumnList[$newColumnkey];
384 |
385 |
386 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] != "PRI" && $column["Key"] == "PRI") {
387 | $add_primary_key = ", add PRIMARY KEY (`".$column["Field"]."`)";
388 | $auto_increment_sub_query = " AUTO_INCREMENT";
389 |
390 | // echo "lofty yessssss ".$saved_column_to_alter["Field"];
391 | }
392 |
393 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] == "PRI" && $column["Key"] == "PRI") {
394 | $auto_increment_sub_query = " AUTO_INCREMENT";
395 | }
396 |
397 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] == "PRI") {
398 | $auto_increment_sub_query = " AUTO_INCREMENT";
399 | }
400 |
401 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] == "auto_increment") {
402 | $auto_increment_sub_query = " AUTO_INCREMENT";
403 | }
404 |
405 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` ".$new_column?->type."(".$new_column->limit.") NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
406 |
407 | if ($new_column?->type == "text") {
408 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` ".$new_column?->type." NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
409 | } else if ($new_column?->type == "double") {
410 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` ".$new_column?->type." NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
411 | } else if ($new_column?->type == "datetime") {
412 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` DATETIME NOT NULL DEFAULT ". $column["Default"]. "; ";
413 | }
414 |
415 | array_push($this->modificationList,
416 | [
417 | "sql"=>$mod_sql,
418 | "from"=>$saved_column_to_alter["Field"],
419 | "to"=>$column["Field"],
420 | "from_type"=>$saved_column_to_alter["Type"],
421 | "to_type"=>$column["Type"],
422 | "operation_type"=>"ALTER_CHANGE_COLUMN"
423 | ]
424 | );
425 |
426 |
427 | if ($column["Extra"] != "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] != "PRI" && $column["Key"] == "PRI") {
428 | array_push($this->modificationList,
429 | [
430 | "sql"=>"ALTER TABLE `".$this->tableName."` ADD PRIMARY KEY(`".$column["Field"]."`);",
431 | "operation_type"=>"ALTER_ADD_PRIMARY_KEY"
432 | ]
433 | );
434 | }
435 |
436 | if ($column["Extra"] != "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] == "PRI" && $column["Key"] != "PRI") {
437 |
438 | array_push($this->columnDropList,
439 | [
440 | "sql"=>"ALTER TABLE `".$this->tableName."` DROP PRIMARY KEY;",
441 | "operation_type"=>"ALTER_DROP_PRIMARY_KEY"
442 | ]
443 | );
444 | }
445 |
446 | /*
447 | * Check if new column has a primary key
448 | */
449 |
450 | if ($column["Extra"] != "auto_increment" && $saved_column_to_alter["Extra"] == "auto_increment" && $saved_column_to_alter["Key"] == "PRI" && $column["Key"] != "PRI") {
451 |
452 |
453 | /*
454 | * Remove auto increment from current column
455 | */
456 | array_push($this->columnDropList,
457 | [
458 | "sql"=>"ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$saved_column_to_alter["Field"]."` ".$saved_column_to_alter["Type"]." NOT NULL; ",
459 | "from"=>$saved_column_to_alter["Field"],
460 | "to"=>$saved_column_to_alter["Field"],
461 | "operation_type"=>"ALTER_CHANGE_COLUMN"
462 | ]
463 | );
464 |
465 | /*
466 | * Remove primary key from table
467 | */
468 | array_push($this->columnDropList,
469 | [
470 | "sql"=>"ALTER TABLE `".$this->tableName."` DROP PRIMARY KEY;",
471 | "operation_type"=>"ALTER_DROP_PRIMARY_KEY"
472 | ]
473 | );
474 | }
475 | }
476 | }
477 |
478 | // echo print_r($this->modificationList);
479 | }
480 |
481 | public function getColumnType(string $field) {
482 | $field_arry = explode("(", $field);
483 |
484 | if (str_contains($field, "(")) {
485 | return (object) ['type'=>$field_arry[0], 'limit'=> str_replace(")", "", $field_arry[1])];
486 | } else {
487 | return (object) ['type'=>$field, 'limit'=> 1];
488 | }
489 |
490 | }
491 |
492 | public function compareAndAlterExsitingColumns() {
493 | // echo "\n\n\n\n\e[0;35;35m new: ". print_r($this->newColumnList) ." in " .$this->tableName." \e[0m\n\n\n\n\n";
494 | // echo "\n\n\n\n\e[0;35;35m saved: ". print_r($this->savedColumnList) ." in " .$this->tableName." \e[0m\n\n\n\n\n";
495 |
496 | $this->filterColumnsToDrop();
497 |
498 | $saved_table_length = count($this->savedColumnList);
499 | foreach ($this->newColumnList as $newColumnkey => $column) {
500 | $new_column = $this->getColumnType($column["Type"]);
501 |
502 | $auto_increment_sub_query = "";
503 | $add_primary_key = "";
504 |
505 | if ($newColumnkey < $saved_table_length) {
506 | $saved_column_to_alter = $this->savedColumnList[$newColumnkey];
507 |
508 |
509 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] != "PRI" && $column["Key"] == "PRI") {
510 | $add_primary_key = ", add PRIMARY KEY (`".$column["Field"]."`)";
511 | $auto_increment_sub_query = " AUTO_INCREMENT";
512 | }
513 |
514 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] == "PRI" && $column["Key"] == "PRI") {
515 | $auto_increment_sub_query = " AUTO_INCREMENT";
516 | }
517 |
518 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] == "PRI") {
519 | $auto_increment_sub_query = " AUTO_INCREMENT";
520 | }
521 |
522 | if ($column["Extra"] == "auto_increment" && $saved_column_to_alter["Extra"] == "auto_increment") {
523 | $auto_increment_sub_query = " AUTO_INCREMENT";
524 | }
525 |
526 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` ".$new_column?->type."(".$new_column->limit.") NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
527 |
528 | if ($new_column?->type == "text") {
529 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` ".$new_column?->type." NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
530 | } else if ($new_column?->type == "double") {
531 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` ".$new_column?->type." NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
532 | } else if ($new_column?->type == "datetime") {
533 | $mod_sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$column["Field"]."` DATETIME NOT NULL DEFAULT ". $column["Default"]. "; ";
534 | }
535 |
536 | array_push($this->modificationList,
537 | [
538 | "sql"=>$mod_sql,
539 | "from"=>$saved_column_to_alter["Field"],
540 | "to"=>$column["Field"],
541 | "from_type"=>$saved_column_to_alter["Type"],
542 | "to_type"=>$column["Type"],
543 | "operation_type"=>"ALTER_CHANGE_COLUMN"
544 | ]
545 | );
546 |
547 |
548 | if ($column["Extra"] != "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] != "PRI" && $column["Key"] == "PRI") {
549 | array_push($this->modificationList,
550 | [
551 | "sql"=>"ALTER TABLE `".$this->tableName."` ADD PRIMARY KEY(`".$column["Field"]."`);",
552 | "operation_type"=>"ADD_PRIMARY_KEY"
553 | ]
554 | );
555 | }
556 |
557 | if ($column["Extra"] != "auto_increment" && $saved_column_to_alter["Extra"] != "auto_increment" && $saved_column_to_alter["Key"] == "PRI" && $column["Key"] != "PRI") {
558 |
559 | array_push($this->modificationList,
560 | [
561 | "sql"=>"ALTER TABLE `".$this->tableName."` DROP PRIMARY KEY;",
562 | "operation_type"=>"DROP_PRIMARY_KEY"
563 | ]
564 | );
565 | }
566 |
567 | /*
568 | * Check if new column has a primary key
569 | */
570 |
571 | if ($column["Extra"] != "auto_increment" && $saved_column_to_alter["Extra"] == "auto_increment" && $saved_column_to_alter["Key"] == "PRI" && $column["Key"] != "PRI") {
572 |
573 |
574 | /*
575 | * Remove auto increment from current column
576 | */
577 | array_push($this->columnDropList,
578 | [
579 | "sql"=>"ALTER TABLE `".$this->tableName."` CHANGE `".$saved_column_to_alter["Field"]."` `".$saved_column_to_alter["Field"]."` ".$saved_column_to_alter["Type"]." NOT NULL; ",
580 | "from"=>$saved_column_to_alter["Field"],
581 | "to"=>$saved_column_to_alter["Field"],
582 | "operation_type"=>"ALTER_CHANGE_COLUMN"
583 | ]
584 | );
585 |
586 | /*
587 | * Remove primary key from table
588 | */
589 | array_push($this->columnDropList,
590 | [
591 | "sql"=>"ALTER TABLE `".$this->tableName."` DROP PRIMARY KEY;",
592 | "operation_type"=>"ALTER_DROP_PRIMARY_KEY"
593 | ]
594 | );
595 | }
596 | } else {
597 | $previous_column_field = end($this->newColumnList)["Field"];
598 | $alter_after_sub_query = "";
599 | if ($newColumnkey != 0) {
600 | $previous_column_field = $this->getPreviousColumn($this->newColumnList, $newColumnkey)["Field"];
601 | $alter_after_sub_query = "AFTER `".$previous_column_field."`";
602 | } else {
603 | $alter_after_sub_query = "FIRST";
604 | }
605 |
606 | if ($column["Extra"] == "auto_increment") {
607 | $auto_increment_sub_query = " AUTO_INCREMENT ";
608 | $add_primary_key = " , add PRIMARY KEY (`".$column["Field"]."`)";
609 | }
610 |
611 | $mod_sql = "ALTER TABLE `".$this->tableName."` ADD `".$column["Field"]."` ".$new_column?->type."(".$new_column->limit.") NOT NULL ".$auto_increment_sub_query." ".$alter_after_sub_query." ". $add_primary_key."; ";
612 |
613 | if ($new_column?->type == "text") {
614 | $mod_sql = "ALTER TABLE `".$this->tableName."` ADD `".$column["Field"]."` ".$new_column?->type." NOT NULL ".$auto_increment_sub_query." ".$alter_after_sub_query." ". $add_primary_key."; ";
615 | } else if ($new_column?->type == "double") {
616 | $mod_sql = "ALTER TABLE `".$this->tableName."` ADD `".$column["Field"]."` ".$new_column?->type." NOT NULL ".$auto_increment_sub_query." ".$alter_after_sub_query." ". $add_primary_key."; ";
617 | } else if ($new_column?->type == "datetime") {
618 | $mod_sql = "ALTER TABLE `".$this->tableName."` ADD `".$column["Field"]."` ".$new_column?->type." NOT NULL DEFAULT CURRENT_TIMESTAMP ".$alter_after_sub_query.";";
619 | }
620 |
621 | array_push($this->modificationList,
622 | [
623 | "sql"=>$mod_sql,
624 | "operation_type"=> "ALTER_ADD_COLUMN"
625 | ]
626 | );
627 |
628 |
629 | if ($column["Extra"] != "auto_increment" && $column["Key"] == "PRI") {
630 | array_push($this->modificationList,
631 | [
632 | "sql"=> "ALTER TABLE `".$this->tableName."` ADD PRIMARY KEY(`".$column["Field"]."`);",
633 | "operation_type"=>"ALTER_ADD_PRIMARY_KEY"
634 | ]
635 | );
636 | }
637 | }
638 | }
639 |
640 | // echo print_r($this->modificationList);
641 | }
642 |
643 | public function filterColumnsToDrop() {
644 | // Log::error("Checking for columns to drop");
645 | $new_table_length = count($this->newColumnList);
646 | if ($new_table_length < count($this->savedColumnList)) {
647 | Log::warning("Some columns would be dropped");
648 | $colums_to_be_dropped = $this->savedColumnList;
649 | $filtered_column_list = [];
650 |
651 | foreach ($this->savedColumnList as $savedColumnKey => $savedColumn) {
652 | /*
653 | *
654 | * Check for columns that already exists in the table
655 | *
656 | */
657 |
658 | if ($savedColumnKey < $new_table_length) {
659 | // Log::success($savedColumn["Field"] ." will not be droped");
660 | array_push($filtered_column_list, $savedColumn);
661 | unset($colums_to_be_dropped[$savedColumnKey]);
662 | } else {
663 | // Log::error($savedColumn["Field"] ." will be droped");
664 | }
665 | }
666 |
667 | foreach ($colums_to_be_dropped as $colum_to_be_dropped) {
668 | array_push($this->modificationList,
669 | [
670 | "sql"=>"ALTER TABLE `".$this->tableName."` DROP `".$colum_to_be_dropped["Field"]."; ",
671 | "operation_type"=>"ALTER_DROP"
672 | ]
673 | );
674 | }
675 |
676 | $this->savedColumnList = $filtered_column_list;
677 |
678 | } else {
679 | // Log::error("Can not perform dropping operation");
680 | }
681 |
682 |
683 | // Log::error("Found columns ".count($this->columnDropList)." to drop");
684 |
685 | // echo print_r($this->columnDropList);
686 | }
687 |
688 | public function getPreviousColumn($arr, $key) {
689 | return $arr[$key-1];
690 | }
691 |
692 | public function getNewColumns() {
693 | $this->compareAndAlterExsitingColumns();
694 | }
695 |
696 | public function clearColumn(string $from, string $to, string $fromType = "", string $toType = "") {
697 | $raw_fromType = $this->getColumnType($fromType);
698 | $raw_toType = $this->getColumnType($toType);
699 | // echo "to: $to ($raw_toType->type) from: $from ($raw_fromType->type) \n";
700 | if ($raw_fromType->type != "" && $raw_toType->type != "" && $raw_fromType->type != $raw_toType->type) {
701 | if (in_array(strtolower($raw_toType->type), ["int", "bigint", "double"]) && in_array(strtolower($raw_fromType->type), ["varchar", "text", "datetime"])) {
702 | $update_sql = "UPDATE `".$this->tableName."` SET `".$from."`=0";
703 | // echo "$$update_sql\n";
704 | // Log::warning($update_sql);
705 | DB::query($update_sql);
706 | } else {
707 | if (in_array(strtolower($raw_toType->type), ["datetime"])) {
708 | $update_sql = "UPDATE `".$this->tableName."` SET `".$from."`='0000-00-00 00:00:00'";
709 | // echo "$$update_sql\n";
710 | // Log::warning($update_sql);
711 | DB::query($update_sql);
712 | }
713 | }
714 | }
715 | }
716 |
717 | public function buildAndRunQuery(string $queryType, string $from, string $to, string $type, string $limit, string $fromType = "", string $toType = "", string $extras = "", string $key = "") {
718 |
719 | $auto_increment_sub_query = "";
720 | $add_primary_key = "";
721 |
722 | if ($extras == "auto_increment") {
723 | $add_primary_key = ", add PRIMARY KEY (`".$to."`)";
724 | $auto_increment_sub_query = " AUTO_INCREMENT";
725 | }
726 | if ($key == "PRI") {
727 | $add_primary_key = ", add PRIMARY KEY (`".$to."`)";
728 | $auto_increment_sub_query = " AUTO_INCREMENT";
729 | }
730 |
731 | if ($queryType == "ALTER_CHANGE_COLUMN") {
732 | $sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$from."` `".$to."` ".$type."(".$limit.") NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
733 |
734 | if (in_array($type, ["text", "datetime", "double"])) {
735 | $sql = "ALTER TABLE `".$this->tableName."` CHANGE `".$from."` `".$to."` ".$type." NOT NULL ".$auto_increment_sub_query." ".$add_primary_key."; ";
736 | }
737 | } else {
738 |
739 | }
740 |
741 | // $this->clearColumn(from: $from, to: $to, fromType: $fromType, toType: $toType);
742 |
743 | // echo "$sql \n";
744 | // Log::neutral($sql);
745 |
746 | DB::query($sql);
747 | }
748 | }
--------------------------------------------------------------------------------
/app/Framework/Utilities/BaseUtility.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Framework/Utilities/Encrypt.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Framework/Utilities/HttpRequest.php:
--------------------------------------------------------------------------------
1 | headers = $headers;
14 | return $httpRequest;
15 | }
16 |
17 | private function getHeaders() {
18 | return array_map(function($value, $key) {
19 | return $key.': '.$value;
20 | }, array_values($this->headers), array_keys($this->headers));
21 | }
22 |
23 | public function withBasicAuth(array $basic_auth) : self {
24 | $this->basic_auth = (object) $basic_auth;
25 | return $this;
26 | }
27 |
28 | private function resolveQueryParams(array $params) {
29 | $iteration = 0;
30 | $rsolvedQuery = "";
31 | forEach ($params as $key => $value) {
32 | if ($iteration == 0) {
33 | $key_encoded = urlencode($key);
34 | $value_encoded = urlencode($value);
35 | $rsolvedQuery = "?$key_encoded=$value_encoded";
36 | } else {
37 | $key_encoded = urlencode($key);
38 | $value_encoded = urlencode($value);
39 | $rsolvedQuery = $rsolvedQuery . "&$key_encoded=$value_encoded";
40 | }
41 | $iteration = $iteration + 1;
42 | }
43 |
44 | return $rsolvedQuery;
45 | }
46 |
47 |
48 | public function resolveRequest($type = "GET", array $data = [], $query = []) {
49 | $query = $this->resolveQueryParams($query);
50 |
51 |
52 | $use_url_post_fields = false;
53 |
54 | $headers = (object) $this->headers;
55 |
56 | if (isset($headers?->{'Content-Type'}) && $headers?->{'Content-Type'} == "application/x-www-form-urlencoded") {
57 | $query = substr($query, 1);
58 | $use_url_post_fields = true;
59 | }
60 |
61 |
62 | $init_request = curl_init( !$use_url_post_fields ? $this->request_url .$query : $this->request_url);
63 |
64 | if ($type != "GET") {
65 | $payload = json_encode($data);
66 |
67 | // Response::send(["choke" => $use_url_post_fields ? $query : $payload]);
68 | curl_setopt( $init_request, CURLOPT_POSTFIELDS, $use_url_post_fields ? $query : $payload);
69 | }
70 |
71 | curl_setopt($init_request, CURLOPT_CUSTOMREQUEST, $type);
72 |
73 | if ($this->headers) {
74 | curl_setopt( $init_request, CURLOPT_HTTPHEADER, $this->getHeaders());
75 | }
76 |
77 | if ($this->basic_auth) {
78 | curl_setopt( $init_request, CURLOPT_USERPWD, $this->basic_auth?->username . ":" . $this->basic_auth?->password);
79 | }
80 |
81 | curl_setopt( $init_request, CURLOPT_RETURNTRANSFER, true );
82 | $result = curl_exec($init_request);
83 |
84 | $err = curl_error($init_request);
85 | curl_close($init_request);
86 | $status_code = curl_getinfo($init_request, CURLINFO_HTTP_CODE);
87 |
88 | $response_data = json_decode($result);
89 |
90 | if ($err) {
91 | throw new Exception($err);
92 | } else {
93 | return (object) array('data'=>$response_data, 'status_code' => $status_code);
94 | }
95 | }
96 |
97 | public function post(string $url, array $data = [], $query = []) {
98 | $this->request_url = $url;
99 | return $this->resolveRequest("POST", $data, $query);
100 | }
101 |
102 | public function patch(string $url, array $data = [], $query = []) {
103 | $this->request_url = $url;
104 | return $this->resolveRequest("PATCH", $data, $query);
105 | }
106 |
107 | public function put(string $url, array $data = [], $query = []) {
108 | $this->request_url = $url;
109 | return $this->resolveRequest("PUT", $data, $query);
110 | }
111 |
112 | public function delete(string $url, array $data = [], $query = []) {
113 | $this->request_url = $url;
114 | return $this->resolveRequest("DELETE", $data, $query);
115 | }
116 |
117 |
118 |
119 | public function get(string $url, $query = []) {
120 | $this->request_url = $url;
121 | return $this->resolveRequest("GET", [] , $query);
122 | }
123 | }
--------------------------------------------------------------------------------
/app/Framework/Utilities/JwtUtility.php:
--------------------------------------------------------------------------------
1 | 'HS256','typ'=>'JWT');
24 | $payload = $user_data;
25 |
26 | $jwt = self::generate_jwt($headers, $payload, $salt ?? env("SERVER_SALT"));
27 |
28 | return $jwt;
29 | }
30 |
31 | public static function validate_token($token) {
32 | // split the jwt
33 | $tokenParts = explode('.', $token);
34 |
35 | if (count($tokenParts) == 3) {
36 | $header = base64_decode($tokenParts[0]);
37 | $payload = base64_decode($tokenParts[1]);
38 | $signature_provided = $tokenParts[2];
39 |
40 | if (isset(json_decode($payload)->exp)) {
41 | // check the expiration time - note this will cause an error if there is no 'exp' claim in the jwt
42 | $expiration = json_decode($payload)->exp;
43 | $is_token_expired = ($expiration - time()) < 0;
44 |
45 | // build a signature based on the header and payload using the secret
46 | $base64_url_header = self::base64url_encode($header);
47 | $base64_url_payload = self::base64url_encode($payload);
48 | $signature = hash_hmac('SHA256', $base64_url_header . "." . $base64_url_payload, env("SERVER_SALT"), true);
49 | $base64_url_signature = self::base64url_encode($signature);
50 |
51 | // verify it matches the signature provided in the jwt
52 | $is_signature_valid = ($base64_url_signature === $signature_provided);
53 |
54 | if ($is_token_expired || !$is_signature_valid) {
55 | return false;
56 | } else {
57 | return true;
58 | }
59 | } else {
60 | return false;
61 | }
62 | } else {
63 | return false;
64 | }
65 | }
66 |
67 | public static function getUserDetails($token) {
68 | // split the jwt
69 | $tokenParts = explode('.', $token);
70 |
71 | if (count($tokenParts) == 3) {
72 | $payload = base64_decode($tokenParts[1]);
73 | $user_details = json_decode($payload);
74 | return $user_details;
75 | }
76 |
77 | return false;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/Framework/Utilities/Log.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Framework/Utilities/Request.php:
--------------------------------------------------------------------------------
1 | params = (object) $params;
15 | $this->query = (object) $query;
16 | $this->headers = (object) $headers;
17 | $this->path = $path;
18 | $this->method = strtolower($method);
19 | $this->body = (object) json_decode(json_encode($body), false);
20 | $this->extras = (object) [];
21 | }
22 |
23 | public function setExtras(array $data) {
24 | $this->extras = (object) $data;
25 | }
26 | }
27 |
28 | ?>
--------------------------------------------------------------------------------
/app/Framework/Utilities/Response.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Framework/Utilities/Upload.php:
--------------------------------------------------------------------------------
1 | $userid, ':source'=>$fileNameNew));
31 | }
32 |
33 | if ($imgType == "fearured_image_update") {
34 | $fileDestination = '../img/'.$fileNameNew;
35 | move_uploaded_file($fileTmpName, $fileDestination);
36 | DB::query('UPDATE posts SET featured_image=:source WHERE id=:postid', array(':postid'=>$userid, ':source'=>$fileNameNew));
37 | }
38 |
39 | if ($imgType == "profilePicture") {
40 | $fileDestination = 'img/'.$fileNameNew;
41 | move_uploaded_file($fileTmpName, $fileDestination);
42 | DB::query('UPDATE users SET avatar=:source WHERE id=:userid', array(':userid'=>$userid, ':source'=>$fileNameNew));
43 | }
44 |
45 | if ($imgType == "contestUserPhoto") {
46 | $fileDestination = 'img/'.$fileNameNew;
47 | move_uploaded_file($fileTmpName, $fileDestination);
48 | DB::query('UPDATE applications SET contest_user_photo=:source WHERE user_id=:userid AND contest_id=:contestid AND status = "pending" ORDER BY id DESC', array(':userid'=>$userid, ':source'=>$fileNameNew, ':contestid'=>intval($contestid)));
49 | }
50 |
51 | if ($imgType == "profilePictureAdmin") {
52 | $fileDestination = '../img/'.$fileNameNew;
53 | move_uploaded_file($fileTmpName, $fileDestination);
54 | DB::query('UPDATE users SET avatar=:source WHERE id=:userid', array(':userid'=>$userid, ':source'=>$fileNameNew));
55 | }
56 |
57 | if ($imgType == "profilePictureAdminContest") {
58 | $fileDestination = '../img/'.$fileNameNew;
59 | move_uploaded_file($fileTmpName, $fileDestination);
60 | DB::query('UPDATE contests SET photo=:source WHERE id=:userid', array(':userid'=>$userid, ':source'=>$fileNameNew));
61 | }
62 |
63 | if ($imgType == "contestPhoto") {
64 | $fileDestination = '../img/'.$fileNameNew;
65 | move_uploaded_file($fileTmpName, $fileDestination);
66 | DB::query('UPDATE contests SET photo=:source WHERE id=:userid', array(':userid'=>$userid, ':source'=>$fileNameNew));
67 | }
68 |
69 | } else {
70 | echo "Your file is too big!";
71 | }
72 | } else {
73 | echo "There was an error uploading your file";
74 | }
75 | } else {
76 | //echo "You cannot upload files of this type";
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/app/Middlewares/Authentication.php:
--------------------------------------------------------------------------------
1 | 'Unauthorized user',
13 | 'status'=>2
14 | );
15 |
16 | return Response::send($response, 401);
17 | }
18 |
19 | if (count(explode(' ', apache_request_headers()['Authorization'])) <= 1) {
20 | $response = array(
21 | 'message'=>'Could not find the token',
22 | 'status'=>2,
23 | );
24 |
25 | return Response::send($response, 401);
26 | }
27 |
28 | $req_token = explode(' ', apache_request_headers()['Authorization'])[1];
29 |
30 | if (JwtUtility::validate_token($req_token)) {
31 | $user_id = JwtUtility::getUserDetails($req_token)->user_id;
32 |
33 | $request->setExtras(['user_id'=> $user_id]);
34 | return $request;
35 | } else {
36 | $response = array(
37 | 'message'=>'Unauthorized user',
38 | 'status'=>2
39 | );
40 | return Response::send($response, 401);
41 | }
42 |
43 | }
44 | }
45 |
46 | ?>
--------------------------------------------------------------------------------
/app/Models/UserModel.php:
--------------------------------------------------------------------------------
1 | id();
14 | $schema->string('username');
15 | $schema->string('firstname');
16 | $schema->string('lastname');
17 | $schema->string('password');
18 | $schema->string('email');
19 | $schema->create();
20 | }
21 | }
22 |
23 | ?>
--------------------------------------------------------------------------------
/app/View.php:
--------------------------------------------------------------------------------
1 | buildStyles($style_contents);
67 |
68 | return "";
69 | }, $processedContent);
70 |
71 | echo $processedContent;
72 | }
73 |
74 | private static $iteration = 0;
75 |
76 | public static function asignElementId(string $htmlContent) {
77 | // matches for html opening or self enclosed tags
78 | $match = "/(\s*<\w+\s*)(((\w*=\".*\"\s*)|(\w*='.*'\s*))*)(\s*)(>|\/>)/";
79 |
80 | $processedContent = preg_replace_callback($match, function ($matches) {
81 | ++self::$iteration;
82 | $elementId = BaseUtility::generateRandomString(6);
83 |
84 | $newhtmlTag = $matches[1] . ' spkId="' . self::$iteration . '-'.$elementId.'"' . " " .$matches[2] . " " . $matches[7];
85 | return $newhtmlTag;
86 | }, $htmlContent);
87 |
88 | return $processedContent;
89 | }
90 |
91 |
92 | private static $dom_updates = "";
93 | public static function compileDomUpdates(string $content) {
94 | error_log("compileDomUpdates running ...");
95 |
96 | $processedContent = preg_replace_callback("/(<\/\w*>|<\w>)/", function ($matches) {
97 | return $matches[0] . "\n";
98 | }, $content);
99 |
100 | // error_log($processedContent);
101 |
102 | $match = "/((\s*<\w+\s*)(((\w*=\".*\"\s*)|(\w*='.*'\s*))*)(\s*)(>|\/>))(.*)(\{\s*(\w+)\s*\}(.*))(<\/\w*>|<\w>)/";
103 |
104 | $processedContent = preg_replace_callback($match, function ($matches) {
105 |
106 | $attribute_match = "/(spkId=\")([\w-]*)(\")/";
107 |
108 | $has_match = preg_match($attribute_match, $matches[3], $attr_matches);
109 |
110 | if ($has_match) {
111 | $state_key = $matches[11];
112 | $state_list = array_filter(self::$reactive_state_list, function($value) use ($state_key) {
113 | return $value['value'] == $state_key;
114 | });
115 |
116 | if (count($state_list) > 0) {
117 | $update_data = "document.querySelector('[spkId=\"".$attr_matches[2]."\"]').textContent = `".$matches[9]." \${".$state_key."} ".$matches[12]."`;";
118 |
119 | $update_list = self::$reactive_state_list[$state_key]["updates"];
120 | array_push($update_list, $update_data);
121 |
122 | self::$reactive_state_list[$state_key]["updates"] = $update_list;
123 | self::$dom_updates = self::$dom_updates . $update_data;
124 | }
125 | }
126 |
127 | return str_replace($matches[10], '$'.$matches[10], $matches[0]);
128 | }, $processedContent);
129 |
130 |
131 | $processedContent = preg_replace_callback("/(
289 | EOD;
290 |
291 | }, $raw_script_content);
292 |
293 | $render = self::$component_logic;
294 |
295 | echo <<
297 |
298 |
299 |
300 | title
301 |
302 |
303 | $render
304 |
305 |
306 |
307 | EOD;
308 | }
309 |
310 | public function buildStyles(string $content) {
311 | $css_generator = new CssGenerator($content);
312 | return $css_generator->compiled_css;
313 | }
314 |
315 | public static function useLayout(string $viewName, array $data = []) {
316 | $viewName = str_replace('../', '', $viewName);
317 | $layoutContent = self::layoutContent();
318 | $viewContent = self::renderView($viewName);
319 |
320 | $content = str_replace('{{content}}', $viewContent, $layoutContent);
321 | $match = "/(\{\{)(|\s+)([\w]+)(|\s+)(\}\})/";
322 | // $replace_text = '$\3';
323 |
324 |
325 | $processedContent = preg_replace_callback($match, function ($matches) use ($data) {
326 | $text = $matches[3];
327 | return $data[$text] ?? "";
328 | }, $content);
329 |
330 |
331 | echo $processedContent;
332 | }
333 |
334 | protected static function layoutContent() {
335 | ob_start();
336 |
337 | include_once __DIR__ . "/../views/layouts/main.php";
338 |
339 | return ob_get_clean();
340 | }
341 | }
342 | ?>
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sparkle/sparkle",
3 | "description": "Sparkle is a free to use open source php MVC framework for building web applications. It's very simple to work with and has a very low learning curve.",
4 | "type": "project",
5 | "license": "MIT",
6 | "autoload": {
7 | "psr-4": {
8 | "App\\": "app/"
9 | }
10 | },
11 | "authors": [
12 | {
13 | "name": "loftytech",
14 | "email": "58659264+loftytech@users.noreply.github.com"
15 | }
16 | ],
17 | "minimum-stability": "stable",
18 | "require": {}
19 | }
20 |
--------------------------------------------------------------------------------
/core/Route.php:
--------------------------------------------------------------------------------
1 | substr($set_url_arr[$i], 1),
54 | 'index' => $i
55 | ));
56 |
57 | if ($req_url_arr[$i] != '') {
58 | array_push($url_params, array(
59 | 'data'=> $req_url_arr[$i],
60 | 'index' => $i
61 | ));
62 | }
63 |
64 | } else {
65 | $set_url_str = $set_url_str . $set_url_arr[$i];
66 |
67 | if ($req_url_len == $set_url_len) {
68 | $req_url_str = $req_url_str . $req_url_arr[$i];
69 | }
70 | }
71 | }
72 |
73 | if (count($url_params) == count($set_params)) {
74 | for ($i=0; $i < count($set_params); $i++) {
75 | $att_params = array_merge($att_params, array(
76 | $set_params[$i]['data'] => $req_url_arr[$set_params[$i]['index']]
77 | ));
78 | }
79 | }
80 | }
81 |
82 | if ($req_url_len == $set_url_len && strtolower($req_url_str) == strtolower($set_url_str) && count($url_params) == count($set_params) && strtolower($_SERVER['REQUEST_METHOD']) == strtolower($type)) {
83 | $request = new Request(method: $_SERVER['REQUEST_METHOD'], path: $raw_req_url, params: $att_params, query: $_GET, body: array_merge(json_decode(file_get_contents('php://input'), true) ?? [], $_POST), headers: apache_request_headers() ?? []);
84 |
85 | foreach($functions as $key => $function) {
86 |
87 | if (is_callable($function)) {
88 | $returnedData = $function($request);
89 | if ($returnedData != null) {
90 | $request = $returnedData;
91 | } else {
92 | exit;
93 | }
94 | } else if (is_array($function)) {
95 | [$class, $method] = $function;
96 |
97 | if (class_exists($class)) {
98 | $class = new $class();
99 |
100 | if (method_exists($class, $method)) {
101 | $returnedData = call_user_func_array([$class, $method], [$request]);
102 | if ($returnedData != null) {
103 | $request = $returnedData;
104 | } else {
105 | exit;
106 | }
107 | }
108 | }
109 | }
110 | }
111 | exit;
112 |
113 | } else if ($route == "/404") {
114 | $lastMethod = end($functions);
115 | $lastMethod();
116 | exit;
117 | } else {
118 | // echo "Invalid route";
119 | }
120 | }
121 |
122 | public static function set($route, $type, array | callable ...$functions ) {
123 | self::validateRoute($route, $type, ...$functions);
124 | }
125 |
126 | public static function get($route, array | callable ...$functions ) {
127 | self::validateRoute($route, "GET", ...$functions);
128 | }
129 |
130 | public static function post($route, array | callable ...$functions ) {
131 | self::validateRoute($route, "POST", ...$functions);
132 | }
133 | public static function patch($route, array | callable ...$functions ) {
134 | self::validateRoute($route, "PATCH", ...$functions);
135 | }
136 | public static function put($route, array | callable ...$functions ) {
137 | self::validateRoute($route, "PUT", ...$functions);
138 | }
139 | public static function delete($route, array | callable ...$functions ) {
140 | self::validateRoute($route, "DELETE", ...$functions);
141 | }
142 | }
143 |
144 | ?>
--------------------------------------------------------------------------------
/core/env_loader.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 | Options -Indexes
2 |
3 | RewriteEngine On
4 | RewriteCond %{REQUEST_URI} !/css
5 | RewriteCond %{REQUEST_URI} !/uploads
6 | RewriteCond %{REQUEST_URI} !/img
7 | RewriteCond %{REQUEST_URI} !/svg
8 | RewriteCond %{REQUEST_URI} !/lofty-admin
9 | RewriteCond %{REQUEST_URI} !/ads.txt
10 | RewriteCond %{REQUEST_URI} !/favicon.ico
11 | RewriteCond %{REQUEST_URI} !/icon.png
12 | RewriteRule ^([^/]+)/? index.php?url=$1 [L,QSA]
13 |
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
2 |
3 | *{
4 | margin: 0px;
5 | padding: 0px;
6 | list-style:none;
7 | text-decoration:none;
8 | font-family: 'Montserrat', sans-serif;
9 | font-weight: 400;
10 | }
11 | html {
12 | box-sizing: border-box;
13 | }
14 | *, *::before, *::after {
15 | box-sizing: inherit;
16 | }
17 | :root {
18 | --primary-color: #cddd1f;
19 | }
20 | h1, h2, h3 {
21 | font-size: 1.2em;
22 | }
23 |
24 | a {
25 | color: #dd1f6f;
26 | }
27 | body {
28 | background: #eaeaea;
29 | max-width: 100vw;
30 | overflow: hidden;
31 | }
32 | img {
33 | display: block;
34 | max-width: 100%;
35 | }
36 | /* 1.0 Header */
37 |
38 | .wrapper {
39 | display: flex;
40 | justify-content: center;
41 | align-items: center;
42 | flex-direction: column;
43 | background-color: #222;
44 | width: 100vw;
45 | height: 100vh;
46 | padding: 20px;
47 | }
48 | .wrapper h2 {
49 | color: var(--primary-color);
50 | font-weight: 800;
51 | font-size: 3rem;
52 | text-align: center;
53 | }
54 | .wrapper p {
55 | color: #f7f7f7;
56 | margin-top: 40px;
57 | font-weight: 300;
58 | font-size: 14px;
59 | text-align: center;
60 | line-height: 24px;
61 | }
62 |
63 | .meta-links ul {
64 | display: flex;
65 | gap: 20px;
66 | flex-wrap: wrap;
67 | margin-top: 20px;
68 | }
69 | .meta-links ul a {
70 | display: flex;
71 | align-items: center;
72 | gap: 10px;
73 | color: #ffffff;
74 | font-weight: 600;
75 | }
76 | .meta-links ul a span {
77 | text-decoration: underline;
78 | }
79 |
80 | @media screen and (min-width: 720px) {
81 | .wrapper h2 {
82 | font-size: 5rem;
83 | }
84 | }
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | 0,
24 | 'message'=>'not found',
25 | ], 404);
26 | });
27 |
28 |
29 | ?>
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/star:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/storage/private/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/storage/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/views/Home.php:
--------------------------------------------------------------------------------
1 |
2 |
Welcome to
3 |
This is a demo page for illustration purposes. You can edit or remove the file in the views directory.
4 |
5 |
6 |
13 |
--------------------------------------------------------------------------------
/views/Test.php:
--------------------------------------------------------------------------------
1 |
2 |
Welcome to
3 |
This is a demo page for illustration purposes. You can edit or remove the file in the views directory.
4 |
5 |
6 | counter: {counter} lol
7 | update
8 |
9 |
10 |
11 |
{userName}
12 |
13 |
14 |
21 |
22 |
23 |
54 |
55 |
75 |
76 |
--------------------------------------------------------------------------------
/views/layouts/main.php:
--------------------------------------------------------------------------------
1 |
2 | {{content}}
3 |
--------------------------------------------------------------------------------
/views/widgets/footer.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |