{"id":24850209,"url":"https://github.com/osalabs/osafw-php","last_synced_at":"2025-10-14T21:31:06.984Z","repository":{"id":149835782,"uuid":"49492038","full_name":"osalabs/osafw-php","owner":"osalabs","description":"Business Applications Web Framework, PHP","archived":false,"fork":false,"pushed_at":"2025-05-21T20:15:40.000Z","size":4782,"stargazers_count":3,"open_issues_count":11,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-21T21:26:27.970Z","etag":null,"topics":["framework","php","restful"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osalabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2016-01-12T10:15:15.000Z","updated_at":"2024-12-14T16:08:41.000Z","dependencies_parsed_at":"2024-05-02T15:01:51.635Z","dependency_job_id":"30ecf3bc-0351-43ab-a700-14c2d9338a65","html_url":"https://github.com/osalabs/osafw-php","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/osalabs/osafw-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osalabs%2Fosafw-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osalabs%2Fosafw-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osalabs%2Fosafw-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osalabs%2Fosafw-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osalabs","download_url":"https://codeload.github.com/osalabs/osafw-php/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osalabs%2Fosafw-php/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279021373,"owners_count":26087022,"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","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["framework","php","restful"],"created_at":"2025-01-31T13:17:40.068Z","updated_at":"2025-10-14T21:31:04.335Z","avatar_url":"https://github.com/osalabs.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP web framework optimized for building Business Applications\n\nCreated as simplified and lightweight alternative to other PHP frameworks\n\n![image](https://user-images.githubusercontent.com/1141095/75820467-0200b380-5d62-11ea-9340-e0942b460eb1.png)\n\n## Features\n\n- simple and straightforward in development and maintenance\n- MVC-like\n    - code, data, templates are split\n    - code consists of: controllers, models, framework core and optional 3rd party libs\n    - uses [ParsePage template engine](https://github.com/osalabs/parsepage)\n    - data stored by default in MySQL database [using db.php](https://github.com/osalabs/db.php)\n- RESTful with some practical enhancements\n- integrated auth - simple flat access levels auth\n- UI based on [Bootstrap 5](http://getbootstrap.com) with minimal custom CSS and themes support - it's easy to customzie\n  or apply your own theme\n- use of well-known 3rd party libraries: [jQuery](http://jquery.com), [jQuery Form](https://github.com/malsup/form),\n  jGrowl, markdown libs, etc...\n\n## Documentation\n\n### Development/Deployment\n\n1. put contents of `/www` into your webserver's public html folder\n2. edit `/www/php/config.site.php` (or `config.localhost.php` for development)\n3. create database from `/db/fwdatabase.sql`, `/db/lookups.sql` and others (if needed)\n4. open site in your browser and login with credentials as defined in fwdatabase.sql\n5. review log in `/logs/osafw.log`\n\n### Directory structure\n\n```\n/db                  - initial fwdatabase.sql script and update sql scripts\n/logs/osafw.log      - application log (ensure to enable write rights to /logs dir for webserver)\n/www                 - application public root folder\n  /php               - all the PHP code is here\n    /controllers     - your controllers\n    /fw              - framework core libs\n    /models          - your models\n    /vendor          - composer libs\n    /config.*.php    - settings for db connection, mail, logging...\n  /template          - all the html templates\n  /upload            - upload dir for public files\n  /assets            - your web frontend assets\n    /css\n    /fonts\n    /img\n    /js\n  /favicon.ico       - change to your favicon!\n  /robots.txt        - default robots.txt (empty)\n```\n\n### REST mappings\n\nControllers automatically directly mapped to URLs, so developer doesn't need to write routing rules:\n\n- `GET /Controller` - list view `IndexAction()`\n- `GET /Controller/ID` - one record view `ShowAction()`\n- `GET /Controller/new` - one record new form `ShowFormAction()`\n- `GET /Controller/ID/edit` - one record edit form `ShowFormAction()`\n- `GET /Controller/ID/delete` - one record delete confirmation form `ShowDeleteAction()`\n- `POST /Controller` - insert new record `SaveAction()`\n- `PUT /Controller` - update multiple records `SaveMultiAction()`\n- `POST/PUT /Controller/ID` - update record `SaveAction()`\n- `DELETE /Controller/ID` - delete record `DeleteAction()`\n- `GET/POST /Controller/(Something)[/ID]` - call for arbitrary action from the controller `SomethingAction()`\n- `GET/POST /Controller/Something[/ID]` - call for arbitrary action from the controller `SomethingAction()`, in this\n  case action name should be less than 32 characters\n\nFor example `GET /Products` will call `ProductsController.IndexAction()`\nAnd this will cause rendering templates from `/www/template/products/index`\n\nID can be numeric or 32-char string like UUID (without dashes)\nFor example `GET /Products/123` will call `ProductsController.ShowAction(123)`\n\n### Request Flow\n\nhighlighted as bold is where you could place your code.\n\n- `FW.run()`\n    - **`FwHooks.initRequest()`** - place code here which need to be run on request start\n- `fw.dispatch()` - performs REST urls matching and calls controller/action, if no controller found calls\n  `HomeController.NotFoundAction()`, if no requested action found in controller - calls controller action defined in\n  contoller's `route_default_action` (either \"index\" or \"show\")\n    - `fw._auth()`  - check if user can access requested controller/action, also performs basic CSRF validation\n    - `fw.call_controller()`\n        - **`SomeController.init()`** - place code here which need to be run every time request comes to this controller\n        - **`SomeController.SomeAction()`** - your code for particular action\n            - **`SomeModel.someMethod()`** - controllers may call model's methods, place most of your business logic in\n              models\n- `fw.Finalize()`\n\n#### Examples:\n\n- GET /Admin/Users\n    - `FwHooks.initRequest()`\n    - `AdminUsers.init()`\n    - `AdminUsers.IndexAction()`\n    - then ParsePage parses templates from `/template/admin/users/index/`\n\n- GET /Admin/Users/123/edit\n    - `FwHooks.initRequest()`\n    - `AdminUsers.init()`\n    - `AdminUsers.ShowFormAction(123)`\n        - `Users.one(123)`\n    - then ParsePage parses templates from `/template/admin/users/showform/`\n\n- POST /Admin/Users/123\n    - `FwHooks.initRequest()`\n    - `AdminUsers.init()`\n    - `AdminUsers.SaveAction(123)`\n        - `Users.update(123)`\n    - `fw.redirect(\"/Admin/Users/123/edit\")` //redirect back to edit screen after db updated\n\n- GET /Admin/Users/(Custom)/123?param1=1\u0026param2=ABC - controller's custom action (non-standard REST)\n    - `FwHooks.initRequest()`\n    - `AdminUsers.init()`\n    - `AdminUsers.CustomAction(123)` - here you can get params using `reqi(\"param1\") -\u003e 1` and `reqs(\"params\") -\u003e \"ABC\"`\n    - then ParsePage parses templates from `/template/admin/users/custom/` unless you redirect somewhere else\n\n- POST /Admin/Users/(Custom)/123 with posted params `param1=1` and `param2=ABC`\n    - `FwHooks.initRequest()`\n    - `AdminUsers.init()`\n    - `AdminUsers.CustomAction(123)` - here you can still get params using `reqi(\"param1\") -\u003e 1` and\n      `reqs(\"params\") -\u003e \"ABC\"`\n    - then ParsePage parses templates from `/template/admin/users/custom/` unless you redirect somewhere else\n\n#### Flow in IndexAction\n\nFrequently asked details about flow for the `IndexAction()` (in controllers inherited from `FwAdminController` and\n`FwDynamicController`):\n\n1. `initFilter()` - initializes `this.list_filter` from query string filter params `\u0026f[xxx]=...`, note, filters\n   remembered in session\n1. `setListSorting()` - initializes `this.list_orderby` based on `list_filter(\"sortby\")` and `list_filter(\"sortdir\")`,\n   also uses `this.list_sortdef` and `this.list_sortmap` which can be set in controller's `init()` or in `config.json`\n1. `setListSearch()` - initializes `this.list_where` based on `list_filter(\"s\")` and `this.search_fields`\n1. `setListSearchStatus()` - add to `this.list_where` filtering by `status` field if such field defined in the\n   controller's model\n1. `getListRows()` - query database and save rows to `this.list_rows` (only current page based on\n   `this.list_filter(\"pagenum\")` and `this.list_filter(\"pagesize\")`). Also sets `this.list_count` to total rows matched\n   by filters and `this.list_pager` for pagination if there are more than one page. Uses `this.list_view`,\n   `this.list_where`, `this.list_orderby`\n\nYou could either override these particular methods or whole `IndexAction()` in your specific controller.\n\nThe following controller fields used above can be defined in controller's `init()` or in `config.json`:\n\n- `this.list_sortdef` - default list sorting in format: \"sort_name[ asc|desc]\"\n- `this.list_sortmap` - mapping for sort names (from `list_filter[\"sortby\"]`) to actual db fields, Hashtable\n  `sort_name =\u003e db_field_name`\n- `this.search_fields` - search fields, space-separated\n- `this.list_view` - table/view to use in `getListRows()`, if empty model's `table_name` used\n\n### fw.config\n\nApplication configuration available via `fw.config-\u003e[SettingName]`.\nMost of the global settings defined in `config.*.php`. But there are several caclulated settings:\n\n| SettingName | Description                                               | Example                                           |\n|-------------|-----------------------------------------------------------|---------------------------------------------------|\n| hostname    | set from server variable HTTP_HOST                        | osalabs.com                                       |\n| ROOT_DOMAIN | protocol+hostname                                         | https://osalabs.com                               |\n| ROOT_URL    | part of the url if Application installed under sub-url    | /suburl if App installed under osalabs.com/suburl |\n| site_root   | physical application path to the root of public directory | C:\\inetpub\\somesite\\www                           |\n| template    | physical path to the root of templates directory          | C:\\inetpub\\somesite\\www\\template                  |\n| log         | physical path to application log file                     | C:\\inetpub\\somesite\\logs\\osafw.log                |\n| tmp         | physical path to the system tmp directory                 | C:\\Windows\\Temp                                   |\n\n### config.json\n\nIn `FwDynamicController` controller behaviour defined by `/template/CONTROLLER/config.json`. Sample file can be fount at\n`/template/admin/demosdynamic/config.json`\nThis config file allows to define/override several properties of the `FwController` (for example: as `model`,\n`save_fields`, `search_fields`, `list_view`,...) as well as define configuration of Show (`show_fields`) and ShowForm (\n`showform_fields`)  screens. Note `is_dynamic_show` and `is_dynamic_showform` should be set to true accordingly.\nThere are samples for the one `show_fields` or `showform_fields` element:\n\n```json\n  //minimal setup to display the field value\n{\n  \"type\": \"plaintext\",\n  \"field\": \"iname\",\n  \"label\": \"Title\"\n},\n```\n\nRenders:\n\n```html\n\n\u003cdiv class=\"form-row\"\u003e\n    \u003clabel class=\"col-form-label\"\u003eTitle\u003c/label\u003e\n    \u003cdiv class=\"col\"\u003e\n        \u003cp class=\"form-control-plaintext\"\u003eFIELD_VALUE\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n```json\n  //more complex - displays dropdown with values from lookup model\n{\n  \"type\": \"select\",\n  \"field\": \"demo_dicts_id\",\n  \"label\": \"DemoDicts\",\n  \"lookup_model\": \"DemoDicts\",\n  \"is_option0\": true,\n  \"class_contents\": \"col-md-3\",\n  \"class_control\": \"on-refresh\"\n},\n```\n\nRenders:\n\n```html\n\n\u003cdiv class=\"form-row\"\u003e\n    \u003clabel class=\"col-form-label\"\u003eDemoDicts\u003c/label\u003e\n    \u003cdiv class=\"col-md-3\"\u003e\n        \u003cselect id=\"demo_dicts_id\" name=\"item[demo_dicts_id]\" class=\"form-control on-refresh\"\u003e\n            \u003coption value=\"0\"\u003e- select -\u003c/option\u003e\n            ... select options from lookup here...\n        \u003c/select\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n| Field name       | Description                                                                                                                                          | Example                                                                                               |\n|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|\n| type             | required, Element type, see values in table below                                                                                                    | select - renders as `\u003cselect\u003e` html                                                                   |\n| field            | Field name from database.table or arbitrary name for non-db block                                                                                    | demo_dicts_id - in case of select id value won't be displayed, but used to select active list element |\n| label            | Label text                                                                                                                                           | Demo Dictionary                                                                                       |\n| lookup_model     | Model name where to read lookup values                                                                                                               | DemoDicts                                                                                             |\n| is_option0       | only for \"select\" type, if true - includes `\u003coption value=\"0\"\u003eoption0_title\u003c/option\u003e`                                                                | false(default),true                                                                                   |\n| is_option_empty  | only for \"select\" type, if true - includes `\u003coption value=\"\"\u003eoption0_title\u003c/option\u003e`                                                                 | false(default),true                                                                                   |\n| option0_title    | only for \"select\" type for is_option0 or is_option_empty option title                                                                                | \"- select -\"(default)                                                                                 |\n| required         | make field required (both client and server-side validation), for `showform_fields` only                                                             | false(default),true                                                                                   |\n| maxlength        | set input's maxlength attribute, for `showform_fields` only                                                                                          | 10                                                                                                    |\n| max              | set input type=\"number\" max attribute, for `showform_fields` only                                                                                    | 999                                                                                                   |\n| min              | set input type=\"number\" min attribute, for `showform_fields` only                                                                                    | 0                                                                                                     |\n| step             | set input type=\"number\" step attribute, for `showform_fields` only                                                                                   | 0.1                                                                                                   |\n| placeholder      | set input's maxlength attribute, for `showform_fields` only                                                                                          | \"Enter value here\"                                                                                    |\n| autocomplete_url | type=\"autocomplete\". Input will get data from `autocomplete_url?q=%QUERY` where %QUERY will be replaced with input value, for `showform_fields` only | /Admin/SomeLookup/(Autocompete)                                                                       |\n| is_inline        | type `radio` or `yesno`. If true - place all options in one line, for `showform_fields` only                                                         | true(default),false                                                                                   |\n| rows             | set textarea rows attribute, for `showform_fields` only                                                                                              | 5                                                                                                     |\n| class            | Class(es) added to the wrapping `div.form-row`                                                                                                       | mb-2 - add bottom margin under the control block                                                      |\n| attrs            | Arbitrary html attributes for the wrapping `div.form-row`                                                                                            | data-something=\"123\"                                                                                  |\n| class_label      | Class(es) added to the `label.col-form-label`                                                                                                        | col-md-3(default) - set label width                                                                   |\n| class_contents   | Class(es) added to the `div` that wraps input control                                                                                                | col(default) - set control width                                                                      |\n| class_control    | Class(es) added to the input control to change appearance/behaviour                                                                                  | \"on-refresh\" - forms refreshes(re-submits) when input changed                                         |\n| attrs_control    | Arbitrary html attributes for the input control                                                                                                      | data-something=\"123\"                                                                                  |\n| help_text        | Help text displayed as muted text under control block                                                                                                | \"Minimum 8 letters and digits required\"                                                               |\n| admin_url        | For type=\"plaintext_link\", controller url, final URL will be: \"\u003c~admin_url\u003e/\u003c~lookup_id\u003e\"                                                            | /Admin/SomeController                                                                                 |\n| lookup_id        | to use with admin_url, if link to specific ID required                                                                                               | 123                                                                                                   |\n| att_category     | For type=\"att_edit\", att category new upload will be related to                                                                                      | \"general\"(default)                                                                                    |\n| validate         | Simple validation codes: exists, isemail, isphone, isdate, isfloat                                                                                   | \"exists isemail\" - input value validated if such value already exists, validate if value is an email  |\n\n##### type values\n\n| Type                                                 | Description                                                  |\n|------------------------------------------------------|--------------------------------------------------------------|\n| _available for both show_fields and showform_fields_ |                                                              |\n| plaintext                                            | Plain text                                                   |\n| plaintext_link                                       | Plain text with a link to \"admin_url\"                        |\n| markdown                                             | Markdown text (server-side rendered)                         |\n| noescape                                             | Value without htmlescape                                     |\n| float                                                | Value formatted with 2 decimal digits                        |\n| checkbox                                             | Read-only checkbox (checked if value equal to true value)    |\n| date                                                 | Date in default format - M/d/yyyy                            |\n| date_long                                            | Date in logn forma - M/d/yyyy hh:mm:ss                       |\n| multi                                                | Multi-selection list with checkboxes (read-only)             |\n| att                                                  | Block for displaying one attachment/file                     |\n| att_links                                            | Block for displaying multiple attachments/files              |\n| added                                                | Added on date/user block                                     |\n| updated                                              | Updated on date/user block                                   |\n| _available only showform_fields_                     |                                                              |\n| group_id                                             | ID with Submit/Cancel buttons block                          |\n| group_id_addnew                                      | ID with Submit/Submit and Add New/Cancel buttons block       |\n| select                                               | select with options html block                               |\n| input                                                | input type=\"text\" html block                                 |\n| textarea                                             | textaread html block                                         |\n| email                                                | input type=\"email\" html block                                |\n| number                                               | input type=\"number\" html block                               |\n| autocomplete                                         | input type=\"text\" with autocomplete using \"autocomplete_url\" |\n| multicb                                              | Multi-selection list with checkboxes                         |\n| radio                                                | radio options block                                          |\n| yesno                                                | radio options block with Yes(1)/No(2) only                   |\n| cb                                                   | single checkbox block                                        |\n| date_popup                                           | date selection input with popup calendar block               |\n| att_edit                                             | Block for selection/upload one attachment/file               |\n| att_links_edit                                       | Block for selection/upload multiple attachments/files        |\n\n### How to Debug\n\nMain and recommended approach - use `logger()` function, which is globally available.\nExamples: `logger(\"some string to log\", var_to_dump)`, `logger(\"WARN\", \"warning message\")`\nAll logged messages and var content (complex objects will be dumped wit structure when possible) written on debug\nconsole as well as to log file (default `/logs/osafw.log`)\nYou could configure log level in your `config.*.php` - search \"LOG_LEVEL\"\n\nAnother debug functions that might be helpful are:\n\n1. `rw($var)` this function will work like var_dump and just dump variable structure and data to browser (with some\n   formatting)\n2. `rwe($var)` same as above, but immediately die to stop script\n\n### Best Practices / Recommendations\n\n- naming conventions:\n    - table name: `user_lists` (lowercase, underscore delimiters is optional)\n    - model name: `UserLists` (UpperCamelCase)\n    - controller name: `UserListsController` or `AdminUserListsController` (UpperCamelCase with \"Controller\" suffix)\n    - template path: `/template/userlists`\n- keep all paths without trailing slash, use beginning slash where necessary\n- db updates:\n    - first, make changes in `/db/fwdatabase.sql` - this file is used to create db from scratch\n    - then create a file `/db/updates/updYYYY-MM-DD[-123].sql` with all the CREATE, ALTER, UPDATE... - this will allow\n      to apply just this update to existing database instances\n- use `fw.route_redirect()` if you got request to one Controller.Action, but need to continue processing in another\n  Controller.Action\n    - for example, if for a logged user you need to show detailed data and always skip list view - in the\n      `IndexAction()` just use `fw.routeRedirect(\"ShowForm\")`\n- uploads\n    - save all public-readable uploads under `/www/upload` (default, see \"UPLOAD_DIR\" in `config.*.php`)\n    - for non-public uploads use `/upload`\n    - or `S3` model and upload to the cloud\n- put all validation code into controller's `Validate()`. See usage example in `AdminDemosController`\n- use `logger()` and review `/logs/osafw.log` if you stuck\n    - make sure you have \"LOG_LEVEL\" set to \"DEBUG\" in your `config.*.php`\n\n### How to quickly create a Report\n\n- all reports accessed via `AdminReportsController`\n    - `IndexAction` - shows a list of all available reports (basically renders static html template with a link to\n      specific reports)\n    - `ShowAction` - based on passed report code calls related Report model\n- base report model is `FwReports`, major methods (you may override in the specific report):\n    - `getReportFilters()` - set data for the report filters\n    - `getReportData()` - returns report data, usually based on some sql query (see Sample report)\n- `ReportSample` model (in `\\www\\php\\models\\Reports` folder) is a sample report implementation, that can be used as a\n  template to build custom reports\n- basic steps to create a new report:\n    - copy `\\www\\php\\models\\Reports\\Sample.php` to `\\www\\php\\models\\Reports\\Cool.php` (to create Cool report)\n    - edit `Cool.php` and rename \"Sample\" to \"Cool\"\n    - modify `getReportFilters()` to match your report filters\n    - modify `getReportData()` to edit sql query and related post-processing\n    - copy templates folder `\\www\\template\\reports\\sample` to `\\www\\template\\reports\\cool`\n    - edit templates:\n        - `title.html` - report title\n        - `list_filter.html` - for filters\n        - `report_html.html` - for report table/layout/appearance\n    - add link to a new report to `\\www\\template\\reports\\index\\main.html`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosalabs%2Fosafw-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosalabs%2Fosafw-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosalabs%2Fosafw-php/lists"}