{"id":23393019,"url":"https://github.com/dvnc0/gimli-php","last_synced_at":"2025-04-11T11:42:07.845Z","repository":{"id":218480689,"uuid":"739651829","full_name":"dvnc0/gimli-php","owner":"dvnc0","description":"An adaptable micro PHP framework that tries to stay out of your way.","archived":false,"fork":false,"pushed_at":"2024-12-26T06:40:12.000Z","size":159,"stargazers_count":4,"open_issues_count":7,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-08T02:53:14.032Z","etag":null,"topics":["php","php-framework","php-micro-framework","php8"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dvnc0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-06T05:45:38.000Z","updated_at":"2024-12-21T08:17:52.000Z","dependencies_parsed_at":"2024-05-09T06:25:09.707Z","dependency_job_id":"44a12cd1-636a-4a60-9c9a-b546c8e77c42","html_url":"https://github.com/dvnc0/gimli-php","commit_stats":null,"previous_names":["dvnc0/gimli-php"],"tags_count":64,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvnc0%2Fgimli-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvnc0%2Fgimli-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvnc0%2Fgimli-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvnc0%2Fgimli-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dvnc0","download_url":"https://codeload.github.com/dvnc0/gimli-php/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248385539,"owners_count":21094903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","php-framework","php-micro-framework","php8"],"created_at":"2024-12-22T05:17:10.876Z","updated_at":"2025-04-11T11:42:07.824Z","avatar_url":"https://github.com/dvnc0.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GimliDuck ⚔️🦆\nAn adaptable micro PHP framework that tries to stay out of your way.\n\n**Very much a work in progress, use at your own risk...**\n\n**Certainty of death. Small chance of success. What are we waiting for?**\n\n## Installation\n`composer require danc0/gimliduck-php`\n\nCreate a skeleton project with:\n`composer create-project danc0/gimli-skeleton`\n\nAdd the [devtools](https://github.com/dvnc0/gimli-devtools) with `composer require --dev danc0/gimliduck-devtools`\n\nCreate a `.htaccess` file that looks something like this to point requests to your `index.php` file\n\n```\nRewriteEngine On\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule ^ index.php [L]\n```\n\n## Usage\nCreating a GimliDuck application is simple:\n\n```php\ndeclare(strict_types=1);\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Gimli\\Application;\nuse Gimli\\Router\\Route;\n\nApplication::create(__DIR__, $_SERVER);\n\nRoute::get('/', function(){\n\techo \"Hello World\";\n});\n\nApplication::start();\n```\nThat is really all you need to get started. You can add more like a template engine, a config file, etc, but you don't **have** to.\n\n### A more complex example\n```php\n\u003c?php\ndeclare(strict_types=1);\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Gimli\\Application;\nuse App\\Core\\Config;\nuse App\\Core\\Cache;\n\ndefine('APP_ROOT', __DIR__);\n\n$App = Application::create(APP_ROOT, $_SERVER);\n\n// set up your config and add it to the Application\n$config_file = parse_ini_file(APP_ROOT . '/App/Core/config.ini', true);\n$App-\u003eConfig = $App-\u003eInjector-\u003eresolveFresh(Config::class, ['config' =\u003e $config_file]);\n\n// Register a cache class with the Injector\n$App-\u003eInjector-\u003eregister(Cache::class, Cache::getCache($App-\u003eConfig-\u003eadmin_cache));\n\n// Run Application\n$App-\u003erun();\n```\n\nThe `Application` class also registers the basic Event handler and Session class when it is created. Additionally, if your config includes `enable_latte` the Latte template engine will be added to the Application instance using the config value for `template_base_dir` as the template directory.\n\n### Declaring Routes\nBy default Gimli will require any files in the `App/Routes` directory. You can disable this by setting `autoload_routes` to `false` in your config file. You can change the directory by setting the value of `route_directory` in your config file. You can also load additional route files with the following method:\n```php\n// Load routes from a file(s)\n$App-\u003eloadRouteFiles([\n\t'App/routes/web.php',\n]);\n```\nThere are a few things you can do with your route callbacks... pass a string, a callable, or an array. \n```php\n\nRoute::get('/', function(){\n\techo \"Hello World\"\n});\n\n// Single action controller, must use __invoke method\nRoute::get('/', Home_Controller::class);\n// cli routes are single action Job classes\nRoute::cli('build-cache', Cache_Job::class);\n\nRoute::get('/', Home_Controller::class . '@homePage');\n\nRoute::get('/', [Home_Controller::class, 'homePage']);\n```\nAny of those work, it's up to you how you do it.\n\nYou can add middleware if you need some extra defense\n\n```php\nRoute::get('/', [Home_Controller::class, 'homePage'])-\u003eaddMiddleware(Logged_In_Middleware::class);\n```\n\nThat should be a class that implements `Gimli\\Middleware\\Middleware_Interface` which requires a `process` method that returns `Gimli\\Middleware\\Middleware_Response`. Middleware does have access to the Application instance including the Injector and whatever else you decide to set on it.\n\nYou can also add groups, define a default route file that should load, and load additional route files to help organize your routes.\n\nYour routes can also contain variable arguments that meet the following patterns:\n\n```php\nprotected array $patterns = [\n\t':all' =\u003e \"([^/]+)\",\n\t':alpha' =\u003e \"([A-Za-z_-]+)\",\n\t':alphanumeric' =\u003e \"([\\w-]+)\",\n\t':integer' =\u003e \"([0-9_-]+)\",\n\t':numeric' =\u003e \"([0-9_-.]+)\",\n\t':id' =\u003e \"([0-9_-]+)\",\n\t':slug' =\u003e \"([A-Za-z0-9_-]+)\",\n];\n```\nYou will need to add their variable name using the `#` symbol in the route definition. \n\n```php\nRoute::get('/user/:integer#id', [User_Controller::class, 'getUser']);\n```\n\nThis variable name is passed to the router and set as a dependency for your controller method. You should use the defined variable name as an argument in your controller method. The value will be typecast based on the available types to `settype`, possible types:\n\n```\ninteger or int\nfloat or double\nstring\narray\nobject\nboolean or bool\n```\n\nExample controller method:\n\n```php\npublic function getUser(Response $Response, int $id): Response {\n\t// do something with $id\n}\n```\n\nControllers should return a `Gimli\\Http\\Response` object. There are helper methods that return formatted `Response` objects to help limit some conditional logic:\n- `response` A basic Response\n- `redirect` Redirect Response\n- `redirect_on_success` Redirect if the response is successful\n- `redirect_on_failure` Redirect if the response is not successful\n- `json_response` JSON Response\n\nJob files are also given the following arguments `subcommand`, `options`, and `flags`. The `options` argument is an array of arrays containing the name and value. The `flags` argument is an array with the given flags. The `subcommand` argument is just a string if a subcommand was given.\n\n### Dependency Injection\n\nYou can use the built in Injector to bind or register dependencies. You can also resolve dependencies from the Injector. You can add anything you need to the Injector and access it throughout your application through the `Application` instance.\n\nThe built in Injector will autowire classes and resolve dependencies as needed. You can also bind a class to a closure if you need to do some setup before returning the class or register an already created object. The example below shows a single action controller that would be resolved from the Router class. The `__construct` method parameters are resolved from the Injector. \n\n```php\n\u003c?php\ndeclare(strict_types=1);\nnamespace App\\Controllers;\n\nuse App\\Logic\\Dashboard_Logic;\nuse Gimli\\Http\\Response;\nuse Gimli\\Application;\nuse Gimli\\View\\Latte_Engine;\n\nclass Dashboard_Landing_Controller {\n\n\t/**\n\t * Constructor\n\t *\n\t * @param Application $Application\n\t */\n\tpublic function __construct(\n\t\tpublic Application $Application,\n\t\tprotected Dashboard_Logic $Dashboard_Logic,\n\t\tprotected Latte_Engine $View\n\t){\n\t\t//\n\t}\n\n\t/**\n\t * Single action controller call\n\t *\n\t * @return Response\n\t */\n\tpublic function __invoke(Response $Response): Response {\t\t\n\t\t$template_data = $this-\u003eDashboard_Logic-\u003egetTemplateData();\n\t\treturn $Response-\u003esetResponse($this-\u003eView-\u003erender('dashboard/dashboard.latte', $template_data));\n\t}\n}\n```\nThe method parameters are also resolved by the Injector when the Route dispatches the method.\n\nThere are also Injector helper methods that cut down on some inline code. Typically if you wanted to inject a class inline you could do it with `$this-\u003eApplication-\u003eInjector-\u003eresolve(Some_Class::class)` or `Application::get()-\u003eInjector-\u003eresolve(Some_Class::class)`. The methods `resolve` and `resolve_fresh` are available to cut on that inline code.\n\n### Database\nThere is a basic PDO wrapper `Database` as well as a `Pdo_Manager` class you can use to manage database queries. The `Pdo_Manager` class returns and instance of `PDO` and can be used to run queries directly. The `Database` class is a wrapper around the `Pdo_Manager` class and provides some basic query methods. There is also a very basic `Model` base class and additional helper methods for `Database`, like elsewhere these methods handle the dependency injection and call the methods on the injected `Database` class. The helpers are:\n- `fetch_column`\n- `fetch_row` \n- `fetch_all`\n- `row_exists`\n\n### Model Seeders\nThere is a basic seeder class that can be used to seed your database. This relies on attributes placed in the model classes to instruct the `Seeder` how to create the data.\n\n```php\n\u003c?php\ndeclare(strict_types=1);\n\nnamespace Gimli\\Database;\n\nuse Gimli\\Database\\Model;\nuse Gimli\\Database\\Seed;\n\n\nclass User_Model extends Model {\n\t\n\t/**\n\t * @var string $table_name\n\t */\n\tprotected string $table_name = 'users';\n\n\t\n\t/**\n\t * ID\n\t * \n\t * @var int $id \n\t */\n\tpublic $id;\n\n\t/**\n\t * Unique_Id\n\t * \n\t * @var string $unique_id \n\t */\n\t#[Seed(type: 'unique_id', args: ['length' =\u003e 12])]\n\tpublic $unique_id;\n\n\t/**\n\t * Username\n\t * \n\t * @var string $username \n\t */\n\t#[Seed(type: 'username')]\n\tpublic $username;\n\n\t/**\n\t * Email\n\t * \n\t * @var string $email \n\t */\n\t#[Seed(type: 'email')]\n\tpublic $email;\n\n\t/**\n\t * Password\n\t * \n\t * @var string $password \n\t */\n\t#[Seed(type: 'password')]\n\tpublic $password;\n\n\t/**\n\t * Is_Active\n\t * \n\t * @var int $is_active \n\t */\n\t#[Seed(type: 'tiny_int')]\n\tpublic $is_active;\n\n\t/**\n\t * First Name\n\t * \n\t * @var string $first_name \n\t */\n\t#[Seed(type: 'first_name')]\n\tpublic $first_name;\n\n\t/**\n\t * Last Name\n\t * \n\t * @var string $last_name \n\t */\n\t#[Seed(type: 'last_name')]\n\tpublic $last_name;\n\n\t/**\n\t * Status\n\t * \n\t * @var int $status \n\t */\n\t#[Seed(type: 'one_of', args: [0,1])]\n\tpublic $status;\n\n\t/**\n\t * Created_At\n\t * \n\t * @var string $created_at \n\t */\n\t#[Seed(type: 'date', args: ['format' =\u003e 'Y-m-d H:i:s', 'min' =\u003e '2021-01-01', 'max' =\u003e '2021-04-01 00:00:00'])]\n\tpublic $created_at;\n\n\t/**\n\t * Updated_At\n\t * \n\t * @var string $updated_at \n\t */\n\t#[Seed(type: 'date', args: ['format' =\u003e 'Y-m-d H:i:s', 'min' =\u003e '2021-04-01 00:02:00'])]\n\tpublic $updated_at;\n\n\t/**\n\t * bio\n\t * \n\t * @var string $about \n\t */\n\t#[Seed(type: 'paragraph', args: ['count' =\u003e 1])]\n\tpublic $about;\n}\n```\n\nYou can then seed the database with the following code:\n\n```php\nSeeder::make(User_Model::class)\n\t-\u003eseed(123)\n\t-\u003ecount(1)\n\t-\u003ecreate();\n```\n\nInstead of create you can call `getSeededData` to get the data that would be inserted into the database. This is useful for testing or manually loading a Model without saving it. You can also pass a callback method that will be given the data of the initial Model. This helps to seed related data. The callback should return an array of `Seeder` instances.\n\n```php\nSeeder::make(User_Model::class)\n\t-\u003eseed(123)\n\t-\u003ecount(1)\n\t-\u003ecallback(function($data) {\n\t\treturn [\n\t\t\tSeeder::make(User_Hobby_Model::class)-\u003ewith(['user_id' =\u003e $data['id']]),\n\t\t\tSeeder::make(User_Group_Model::class)-\u003ewith(['user_id' =\u003e $data['id']]),\n\t\t]\n\t})\n\t-\u003ecreate();\n```\nThe passed seed ensures the data remains the same each time that seeder is run, resulting in reproducible datasets. The `Seeder` class has a `getRandomSeed` method that will return a random seed value. This is useful for creating random data that you don't need to be reproducible, or to generate a random seed you can copy and use.\n\n### Config Helpers\nThere are also a few Config helpers:\n- `get_config` to get the entire config array\n- `get_config_value` to get a specific value from the config array\n- `config_has` to check if a key exists in the config array","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdvnc0%2Fgimli-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdvnc0%2Fgimli-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdvnc0%2Fgimli-php/lists"}