diff --git a/README.md b/README.md index 8509fe6..c02add2 100644 --- a/README.md +++ b/README.md @@ -1,385 +1,709 @@ -# phacil-framework -A super easy PHP Framework for web development! - -## Requirements - - - PHP 5.3+ - - OPCache PHP Extension - - HTTP Web Server (Apache 2.4+ recomended) - -## Structure - -| Folder | Description | -| ------ | ------ | -| Controller | Contains the structure and files of controllers code | -| Model | Contais directories and files for the model code | -| View | Contains the template files | -| Cache | Full writable default cache storage | -| public_html | Contains the index.php and .htaccess Apache file. All your access public files stay here, like CSS, JavaScript, etc. | -| System | Most important folder of this framework. Contains the libraries, classes and anothers features to improve correct working for the Web App. | -| Logs | Contais debug and error logs | -| config.php | File with contains all basic configurations for the Web App, like DB connection, mail config, directory config, etc.| - -## Get started! - -Copy or clone this repository in your computer/server and edit the config.php file. See below the Config File Basic Options. - -### Hello World! sample - -This is a simple Hello World! for this framework. - -1. In **controller** folder, create a folder called *common* and create a file called *home.php*. -2. Edit **controller/common/home.php** like that. - - ```php - data['variable'] = "Hello World!"; - - $this->out(); - } - } - ``` - -3. Now create a folder insede view called **default** and a subfolder called **common** and a file home.twig. -4. Edit the **view/default/common/home.twig** like this. - - ```html -

{{ variable }}

- ``` -5. Edit the constants *HTTP_URL* and *DIR_APPLICATION* in file **config.php**. Edit others *DIR_** constants path if necessary. - ```php - ' - ExacTI phacil', - 'config_mail_protocol'=>'smtp', - 'config_error_display' => 1, - 'config_template' => "default", - 'config_error_filename'=> 'error.log'); - -//App folders -define('DIR_APPLICATION', '/Applications/MAMP/htdocs/phacil/'); -define('DIR_LOGS', DIR_APPLICATION.'logs/'); -define('DIR_PUBLIC', DIR_APPLICATION.'public_html/'); -define('DIR_SYSTEM', DIR_APPLICATION.'system/'); -define('DIR_IMAGE', DIR_APPLICATION.'public_html/imagens/'); -define('DIR_TEMPLATE', DIR_APPLICATION.'view/'); -define('DIR_CACHE', DIR_APPLICATION.'cache/'); - - - ``` -6. Access your web app in your favorite browser and enjoy this magnific Hello World! - -#### Explanation - -All routes are a mapping class with extends the primary Controller class. In a simple term, the class name in controller is *Controller****Folder****Filename*. - - `class ControllerFolderFile extends Controller { - }` - - The public function named `index()` is a default function to generate the page. - - `public function index() { }` - - Array `$this->data` receives in array format the variables to send to template, in this sample called **variable** with the string *Hello World!*. - - In the last we finish with a simple `$this->out();` to indicate the output and process template file. It's a automated mechanism to link this controller with the respective viewer. - - The viewer in this sample is a twig template format ([Twig page](https://twig.symfony.com)). ExacTI Phacil Framework permits to use varius PHP template engines, like Twig, Mustache, Dwoo and Smarty. - - ## Config file Parameters - - A simple description of default constants configurations in *config.php* file. - - | Constant | Type | Description | Required | - | ----- | ----- | ----- | ----- | - | HTTP_URL | string | Contains the URL for this WEB APP | Yes | - | HTTPS_URL | string | Same this HTTP_URL, but in SSL format | - | HTTP_IMAGE | string | The image URL | - | CDN | string | CDN URL to use for static content | - | DB_CONFIG | boolean | Permit to use configurations direct in database. Requires database instalattion. Values: true or false. | Yes | - | DEBUG | boolean | Indicate the debug mode. | Yes | - | `$configs` | array | No-SQL configs | - | DIR_APPLICATION | string | Absolute path to application folder | Yes | - | DIR_LOGS | string | Path to log folder | - | DIR_PUBLIC | string | Path to the public folder. This directory is a directory to configure in your HTTP server. | Yes | - | DIR_SYSTEM | string | System directory | Yes | - | DIR_IMAGE | string | Directory to store images used by Image library. | - | DIR_TEMPLATE | string | Directory with templates folder | Yes | - | DIR_CACHE | string | Directory to storage the generated caches. | Yes | - | DB_DRIVER | string | Driver to connect a database source | - | DB_HOSTNAME | string | Database host | - | DB_USERNAME | string | Username to connect a database | - | DB_PASSWORD | string | Database password | - | DB_DATABASE | string | Database name | - | SQL_CACHE | boolean | Use the SQL Select cache system | - - ## Template Engines Support - - This framework supports this PHP templates engines: - - TPL (basic template with PHP and HTML); - - [Twig](https://twig.symfony.com); - - [Mustache](https://mustache.github.io); - - [Dwoo](http://dwoo.org); - - [Smarty](https://www.smarty.net). - - To use a determined template engine, just create a file with name of engine in extension, for sample, if you like to use a Twig, the template file is **demo.twig**, if desire the Mustache template, use **demo.mustache** extension file. - The ExacTI Phacil Framework allows to use varius template engines in the same project. - - ## Easy made functions - - This framework is very focused in agile, secutiry and reutilizable code in PHP. Contains a very utily functions we see in the next section. - - ### Database - - To execute a query: - ```php - $variable = $this->db->query("SELECT * FROM mundo"); - ``` - - Withou SQL Cache (if enabled): - ```php - $variable = $this->db->query("SELECT * FROM mundo", false); - ``` -Escape values to more security (no SQL injection issue): - `$this->db->escape($value)` - -To get this rows: - `$variable->rows`; - - Get one row: `$variable->row`; - - Number of rows: `$variable->num_rows`; - - Sample: - ```php - db->query("SELECT * FROM settings WHERE code = '". $this->db->escape($code). "'"); - - $this->data['rows'] = $variable->rows; - $this->data['totalRecords'] = $variable->num_rows; - - return $this->data; - } - } - ``` - - ### Document easy made - - Use the especial Document class to manipulate easily informations about your final HTML. - - To call a document class, use `$this->document` inside Controller. - - To add a CSS: `$this->document->addStyle($href, $rel = 'stylesheet', $media = 'screen', $minify = true)`; - - To add a JavaScript: `$this->document->addScript($script, $sort = '0', $minify = true)`; - -Facebook Metatag: `$this->document->addFBMeta($property, $content = '')`; - -Document HTML Title: `$this->document->setTitle($title)`; - -Page description: `$this->document->setDescription($description)`; - - Sample: - ```php - document->addStyle('pipoca.css'); - } - } - ``` - -### Classes and functions - -This framework have a lot of utilities and accepts much more in system folder with autoload format. - -#### To show all classes - -Execute a Classes() class in one Controller. - - ```php - $cla = new Classes(); - -var_dump($cla->classes()); - ``` -#### To show all classes and functions registereds - - Execute a Classes() class and call functions() in one Controller. - - ```php - $cla = new Classes('HTML'); - -var_dump($cla->functions()); - ``` - - **Note:** *The HTML parameter is the output in HTML style. Is optional.* - - -## Loaders - -In this framework, loaders is a simple way to get resources to use in your PHP code. Is very intuitive and requirew few steps. - -For sample, to load a model, is just `$this->load->model('folder/file');` and to use is `$this->model_folder_file->object();`, like this sample: - ```php - load->model('data/json'); - $this->data['totalData'] = $this->model_data_json->total(); - - $this->out(); - } - } - - ``` - -You can use loaders to: - - Controllers; - - Models; - - Librarys; - - Configs; - - Databases; - - Languages. - -## Models - -This framework is totally MVC (Model, View and Controller) based. The models is just like the controllers and uses the same structure, with a different folder. - -To create a model, put in the models folder a directory and file with the code. - ```php - request` method. For sample, to obtain a POST value, use `$this->request->post['field']` to get the post value with security. - For a \$_SERVER predefined variables, use `$this->request->server['VALUE']` and $this->request->get() for \$_GET values. - The advantages to use this requests instead the predefined variables of PHP is more the more security, upgradable and unicode values. - - ### Sessions - - Sessions is a little different method, you can define and use with `$this->session->data['name']`. - -## Special controller parameters - -| Parameter | Type | Description| -| ----- | ----- | ----- | -| `$this->template` | string | Specify manualy the path to template | -| `$this->twig` | array | Create Twig aditional functions. Eg.: `$this->twig = array('base64' => function($str) { return base64_encode($str); }); `| -| `$this->children` | array | Load in variable other renders of web app. Th childrens *footer* and *header* is default loaded when uses `$this->out()` output. To load without this defaults childrens, use `$this->out(false)`.| -| `$this->redirect($url)` | string | Create a header to redirect to another page. | - - - - - -## Routes - -The ExacTI Phacil Framework is a simple router base to create a simple and consistent web navigation. - -Withou SEO URL, we invoke a page with *route* get parameter when contains a scheme folder/file of controller, like this: *http://example.com/index.php?route=folder/file*. - -In a sample case, we have this controller: - ```php - foo($this->request->get['p']); - } - private function foo($parameter) { - return ($parameter != 0) ? "another" : "other"; - } - } - ``` - - If we access *index.php?route=folder/file* we see the "Index" message. But, like a tree, if we access *index.php?route=folder/file/another* obtains the another function code, with "other" message. - - Multiple Get parameters is simple and we can define normaly, eg.: *index.php?route=folder/file/another&p=2* to print "another" message. - - Private and protected functions is only for internal use, if we tried to access *index.php?route=folder/file/foo*, the framework return 404 HTTP error. - - ### SEO URL - - If you need a beautiful URL for SEO or other optimizations, you can use the url_alias database table. Is very simple and "translate" the routes in URL. - - Imagine this SQL table called url_alias: - - | url_alias_id | query | get | keyword | - | ----- | ----- | ----- | ----- | - | 1 | contact/contato | | contact| - | 2 | webservice/sitemap | | sitemap | - - With the url_alias, the access to route *http://example.com/index.php?route=contact/contato* and *http://example.com/contact* is the same!(and very prety!). - - ### Links - - We have a function to create a strict and dinamic links automatic: `$this->url->link($route, $args = '', $connection = 'NONSSL')`. Is a simple function to generate internal links with correct URL encode. - - In sample of this table above, if we have this code: - ```php - echo $this->url->link('contact/contato'); - ``` - - We get this URL in output: *http://example.com/contact* - - But if you don't have a route in url_alias table, returns the complete route. - - ```php - echo $this->url->link('catalog/products'); - ``` - - Return this URL in output: *http://example.com/index.php?route=catalog/products* - - With extra GET paramethers: - ```php - $fields = array('foo' => 'bar'); - echo $this->url->link('contact/contato', $fields); - echo $this->url->link('contact/contato/place', $fields); - ``` - - We get this URL in output with GET paramethers: - - *http://example.com/contact?foo=bar* - - *http://example.com/index.php?route=contact/contato/place&foo=bar* - - ## License - - This project is manteined for [ExacTI IT Solutions](https://exacti.com.br) with GPLv3 license. \ No newline at end of file +# Phacil-framework +A super easy PHP Framework for web development! + + +## Requirements + + - PHP 5.3+ (PHP 7.0+ recommended) + - OPCache PHP Extension + - HTTP Web Server (Apache 2.4+ recommended) + +## Structure + +| Folder | Description | +| ------ | ------ | +| Controller | Contains the structure and files of controller's code | +| Model | Contains directories and files for the model code | +| View | Contains the template files | +| Cache | Full writable default cache storage | +| public_html | Contains the index.php and .htaccess Apache file. All your access public files stay here, like CSS, JavaScript, etc. | +| System | Most important folder of this framework. Contains the libraries, classes and others features to improve correct working for the Web App. | +| Logs | Contains debug and error logs | +| config.php | File with contains all basic configurations for the Web App, like DB connection, mail config, directory config, etc.| + +## Get started! + +Copy or clone this repository in your computer/server and edit the config.php file. See below the Config File Basic Options. + +### Hello World! sample + +This is a simple Hello World! for this framework. + +1. In **controller** folder, create a folder called *common* and create a file called *home.php*. +2. Edit **controller/common/home.php** like that. + + ```php + data['variable'] = "Hello World!"; + + $this->out(); + } + } + ``` + +3. Now create a folder inside view called **default** and a subfolder called **common** and a file home.twig. +4. Edit the **view/default/common/home.twig** like this. + + ```html +

{{ variable }}

+ ``` +5. Edit the constants *HTTP_URL* and *DIR_APPLICATION* in file **config.php**. Edit others *DIR_** constants path if necessary. + ```php + ' - ExacTI phacil', + 'config_mail_protocol'=>'smtp', + 'config_error_display' => 1, + 'config_template' => "default", + 'config_error_filename'=> 'error.log'); + +//App folders +define('DIR_APPLICATION', '/Applications/MAMP/htdocs/phacil/'); +define('DIR_LOGS', DIR_APPLICATION.'logs/'); +define('DIR_PUBLIC', DIR_APPLICATION.'public_html/'); +define('DIR_SYSTEM', DIR_APPLICATION.'system/'); +define('DIR_IMAGE', DIR_APPLICATION.'public_html/imagens/'); +define('DIR_TEMPLATE', DIR_APPLICATION.'view/'); +define('DIR_CACHE', DIR_APPLICATION.'cache/'); + ``` +6. Access your web app in your favorite browser and enjoy this magnify Hello World! + +#### Explanation + +All routes are a mapping class with extends the primary Controller class. In a simple term, the class name in controller is *Controller****Folder****Filename*. + + `class ControllerFolderFile extends Controller { + }` + + The public function named `index()` is a default function to generate the page. + + `public function index() { }` + + Array `$this->data` receives in array format the variables to send to template, in this sample called **variable** with the string *Hello World!*. + + In the last we finish with a simple `$this->out();` to indicate the output and process template file. It's an automated mechanism to link this controller with the respective viewer. + + The viewer in this sample is a twig template format ([Twig page](https://twig.symfony.com)). ExacTI Phacil Framework permits to use various PHP template engines, like Twig, Mustache, Dwoo and Smarty. + + ## Config file Parameters + + A simple description of default constants configurations in *config.php* file. + + | Constant | Type | Description | Required | + | ----- | ----- | ----- | ----- | + | HTTP_URL | string | Contains the URL for this WEB APP | Yes | + | HTTPS_URL | string | Same this HTTP_URL, but in SSL format | + | HTTP_IMAGE | string | The image URL | + | CDN | string | CDN URL to use for static content | + | DB_CONFIG | boolean | Permit to use configurations direct in database. Requires database installation. Values: true or false. | Yes | + | DEBUG | boolean | Indicate the debug mode. | Yes | + | `$configs` | array | No-SQL configs | + | DIR_APPLICATION | string | Absolute path to application folder | Yes | + | DIR_LOGS | string | Path to log folder | + | DIR_PUBLIC | string | Path to the public folder. This directory is a directory to configure in your HTTP server. | Yes | + | DIR_SYSTEM | string | System directory | Yes | + | DIR_IMAGE | string | Directory to store images used by Image library. | + | DIR_TEMPLATE | string | Directory with templates folder | Yes | + | DIR_CACHE | string | Directory to storage the generated caches. | Yes | + | DIR_CONFIG | string | Directory with extra configs files | + | DB_DRIVER | string | Driver to connect a database source. See below the default's drivers available. | + | DB_HOSTNAME | string | Database host | + | DB_USERNAME | string | Username to connect a database | + | DB_PASSWORD | string | Database password | + | DB_DATABASE | string | Database name | + | SQL_CACHE | boolean | Use the SQL Select cache system | + | ROUTES | array | Specify manually routes | + + ## Outputs and renders + + Phacil Framework count with three methods for output content: `$this->response->setOutput()`, `$this->render()` and `$this->out()`. + + ### setOutput + When you call a `$this->response->setOutput` inside a controller, you specify an output of content to screen without template. It's very useful for JSON, XML or others data contents. + + ##### Sample + ```php + response->setOutput($variable); + // prints "value" in screen + } + } + ``` + + ### render +The `$this->render` just render the controller with template but not output this. +Needs to specify a `$this->template` to associate with template file. +It's much used in children's controllers, like headers and footers. + +##### Sample +```php + template = 'default\common\header.twig'; + + $this->render(); + } + } + ``` +### out + +The `$this->out` is a smart way to output content to screen and combine the render with an associative template. For sample, if you have a controller in a file called `sample.php` inside the `demo` controller folder and a viewer called `sample.twig` inside `demo` view folder, automatically links with one another and the `$this->template` isn't need (Unless you want to specify another template with a different name). +If you specify `$this->out(false)` the auto childrens "header" and "footer" are not loaded. + +For functions inside the controller that are different from index, for automatic mapping with the template it is necessary to add an underscore (_) after the controller name and add the function name (E.g.: contact_work.twig to link with the route common/contact/work, see *Routes* section for more information). + +##### Sample + ```php + document->setTitle('Contact Us'); + + $this->out(); + // automatically link with common/contact.twig template + } + + public function work() { + $this->document->setTitle('Work with Us'); + + $this->out(); + // automatically link with common/contact_work.twig template + } + } + ``` + + + + ## Template Engines Support + + This framework supports this PHP templates engines: + - TPL (basic template with PHP and HTML); + - [Twig](https://twig.symfony.com); + - [Mustache](https://mustache.github.io); + - [Dwoo](http://dwoo.org); + - [Smarty](https://www.smarty.net). + + To use a determined template engine, just create a file with name of engine in extension, for sample, if you like to use a Twig, the template file is **demo.twig**, if desire the Mustache template, use **demo.mustache** extension file. + The ExacTI Phacil Framework allows to use various template engines in the same project. + + ## Easy made functions + + This framework is very focused in agile, security and reusable code in PHP. Contains a very utile functions we see in the next section. + + ### Database + + To execute a query: + ```php + $variable = $this->db->query("SELECT * FROM mundo"); + ``` + + Without SQL Cache (if enabled): + ```php + $variable = $this->db->query("SELECT * FROM mundo", false); + ``` +Escape values to more security (no SQL injection issue): + `$this->db->escape($value)` + +To get these rows: + `$variable->rows`; + + Get one row: `$variable->row`; + + Number of rows: `$variable->num_rows`; + + Sample: + ```php + db->query("SELECT * FROM settings WHERE code = '". $this->db->escape($code). "'"); + + $this->data['rows'] = $variable->rows; + $this->data['totalRecords'] = $variable->num_rows; + + return $this->data; + } + } + ``` + + + #### Supported databases and drivers + + | Driver | Database Type | Description | + | ----- | ----- | ------ | + | db_pdo | MariaDB | Optimized PDO connection to work with MariaDB databases with charset utf8mb4. Also works very well with MySQL.| + | dbmysqli | MySQL/MariaDB | MySQLi PHP connection | + | mssql | MS SQL Server | Use mssql PHP extension for connect to Microsoft SQL Server databases. | + | mpdo | MySQL | PDO connection for MySQL databases | + | mysql | MySQL | Legacy MySQL extension. Works only in PHP 5.| + | oracle | Oracle | Connect to Oracle databases | + | postgre | PostgreSQL | Driver for connect to PostgreSQL databases. | + | sqlsrv | MS SQL Server | Connect a Microsoft SQL Server database with sqlsrv PHP extension. | + | sqlsrvpdo | MS SQL Server | Connect to Microsoft SQL Server using PDO driver. | + + + ### Document easy made + + Use the especial Document class to manipulate easily informations about your final HTML. + + To call a document class, use `$this->document` inside Controller. + + To add a CSS: `$this->document->addStyle($href, $rel = 'stylesheet', $media = 'screen', $minify = true)`; + + To add a JavaScript: `$this->document->addScript($script, $sort = '0', $minify = true)`; + +Facebook Metatag: `$this->document->addFBMeta($property, $content = '')`; + +Document HTML Title: `$this->document->setTitle($title)`; + +Page description: `$this->document->setDescription($description)`; + + Sample: + ```php + document->addStyle('pipoca.css'); + } + } + ``` + +### Classes and functions + +This framework has a lot of utilities and accepts much more in system folder with autoload format. + +#### To show all classes + +Execute a Classes() class in one Controller. + + ```php + $cla = new Classes(); + +var_dump($cla->classes()); + ``` +#### To show all classes and functions registered + + Execute a Classes() class and call functions() in one Controller. + + ```php + $cla = new Classes('HTML'); + +var_dump($cla->functions()); + ``` + + **Note:** *The HTML parameter is the output in HTML style. Is optional.* + + +## Loaders + +In this framework, loaders are a simple way to get resources to use in your PHP code. Is very intuitive and require few steps. + +For sample, to load a model, is just `$this->load->model('folder/file');` and to use is `$this->model_folder_file->object();`, like this sample: + ```php + load->model('data/json'); + $this->data['totalData'] = $this->model_data_json->total(); + + $this->out(); + } + } + + ``` + +You can use loaders to: + - Controllers; + - Models; + - Librarys; + - Configs; + - Databases; + - Languages. + +### Load database connection + +If you need an extra database connection or a different driver/database access, you can use the `$this->load->database($driver, $hostname, $username, $password, $database);` method (see more about databases functions in the Databases section of this document). + +The name of database is a registered object to access database functions. +This load is simple and registry to object of origin. + +##### Sample: + ```php + load->database('mpdo', 'localhost', 'root', 'imtheking', 'nameDatabase'); + + $sql = $this->nameDatabase->query("SELECT * FROM mundo"); + + return $sql->rows; + } + } + ``` + +## Models + +This framework is totally MVC (Model, View and Controller) based. The models are just like the controllers and uses the same structure, with a different folder. + +To create a model, put in the models folder a directory and file with the code. + ```php + load->model('folder/file'); + echo $this->model_folder_file->SayMyName(); + // this output is "Heisenberg". + } + } + ``` + + + ## Constructs and Destructs + + In some cases, we need to add a __construct or __destruct in a class to better code practices. To call a constructs in controllers and models, use the `$registry` parent: + ```php + public function __construct($registry) + { + parent::__construct($registry); + + // YOUR CODE HERE AFTER THIS LINE! + } + ``` + + ## Requests and Sessions + + To use a magic request system, you just need to call a `$this->request` method. For sample, to obtain a POST value, use `$this->request->post['field']` to get the post value with security. + For a \$_SERVER predefined variables, use `$this->request->server['VALUE']` and $this->request->get() for \$_GET values. + The advantages to use this requests instead the predefined variables of PHP are more the more security, upgradable and unicode values. + + ### Sessions + + Sessions is a little different method, you can define and use with `$this->session->data['name']`. + +## Special controller parameters + +| Parameter | Type | Description| +| ----- | ----- | ----- | +| `$this->template` | string | Specify manually the path to template | +| `$this->twig` | array | Create Twig additional functions. E.g.: `$this->twig = array('base64' => function($str) { return base64_encode($str); }); `| +| `$this->children` | array | Load in a template variable other renders of web app. Th children's *footer* and *header* is default loaded when uses `$this->out()` output. To load without this defaults childrens, use `$this->out(false)`.| +| `$this->redirect($url)` | string | Create a header to redirect to another page. | + + +## Routes + +The ExacTI Phacil Framework is a simple router base to create a simple and consistent web navigation. + +Without SEO URL, we invoke a page with *route* get parameter when contains a scheme folder/file of controller, like this: *http://example.com/index.php?route=folder/file*. + +In a sample case, we have this controller: + ```php + foo($this->request->get['p']); + } + private function foo($parameter) { + return ($parameter != 0) ? "another" : "other"; + } + } + ``` + + If we access *index.php?route=folder/file* we see the "Index" message. But, like a tree, if we access *index.php?route=folder/file/another* obtains another function code, with "other" message. + + Multiple Get parameters is simple and we can define normally, e.g.: *index.php?route=folder/file/another&p=2* to print "another" message. + + Private and protected functions is only for internal use, if we tried to access *index.php?route=folder/file/foo*, the framework return 404 HTTP error. + + ### SEO URL + + If you need a beautiful URL for SEO or other optimizations, you can use the url_alias database table. Is very simple and "translate" the routes in URL. + + Execute this SQL in your database to create a url_alias table: + ```SQL + CREATE TABLE `url_alias` ( + `url_alias_id` int(11) NOT NULL AUTO_INCREMENT, + `query` varchar(255) COLLATE utf8_bin NOT NULL, + `get` longtext COLLATE utf8_bin, + `keyword` varchar(255) COLLATE utf8_bin NOT NULL, + PRIMARY KEY (`url_alias_id`), + UNIQUE KEY `keyword` (`keyword`) +) AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + ``` + + Imagine this SQL table called url_alias: + + | url_alias_id | query | get | keyword | + | ----- | ----- | ----- | ----- | + | 1 | contact/contato | | contact| + | 2 | webservice/sitemap | | sitemap | + + With the url_alias, the access to route *http://example.com/index.php?route=contact/contato* and *http://example.com/contact* is the same!(and very pretty!). + + #### SEO URL without Database + + You can create URL without a SQL Database configuration. For this you just need specify routes in the config file using the ***ROUTES*** constant with array content, like this: + + ```php + define('ROUTES', array( + 'magic' => 'common/home/xml', + 'json' => 'common/home/json')); + ``` + + ### Links + + We have a function to create a strict and dynamic links automatic: `$this->url->link($route, $args = '', $connection = 'NONSSL')`. Is a simple function to generate internal links with correct URL encode. + + In sample of this table above, if we have this code: + ```php + echo $this->url->link('contact/contato'); + ``` + + We get this URL in output: *http://example.com/contact* + + But if you don't have a route in url_alias table, returns the complete route. + + ```php + echo $this->url->link('catalog/products'); + ``` + + Return this URL in output: *http://example.com/index.php?route=catalog/products* + + With extra GET parameters: + ```php + $fields = array('foo' => 'bar'); + echo $this->url->link('contact/contato', $fields); + echo $this->url->link('contact/contato/place', $fields); + ``` + + We get this URL in output with GET parameters: + + *http://example.com/contact?foo=bar* + + *http://example.com/index.php?route=contact/contato/place&foo=bar* + + ***Note:*** *It's necessary specify the config `config_seo_url` for correctly function of this URLs. If you use the SQL url_alias table, you need specify the `USE_DB_CONFIG` to true in config file.* + + ## JSON, XML and others custom responses + + If you need to output in another format or another response, you can use the Response method `$this->response`. + + For sample, to create a controller when the output is a JSON data: + + ```php + 482, "id" => 221), array("index" => 566, "id" => 328)); + + $json = json_encode($record); + + $this->response->addHeader('Content-Type: application/json'); + + $this->response->setOutput($json); + } + } + ``` + + The `$this->response->addHeader` is responsible to send a personalized HTTP header to browser, in this case specify the *Content-Type* information. The `$this->response->setOutput()` works for send output of the content of a variable, function, constant, etc., to the browser. + + We have this output: + + ```json + [{"index":482,"id":221},{"index":566,"id":328}] + ``` + + Another sample, but with XML data: + + ```php + 'blub', + 'foo' => 'bar', + ); + $xml = new SimpleXMLElement(''); + array_walk_recursive($test_array, array ($xml, 'addChild')); + + $this->response->addHeader('Content-Type: text/xml'); + + $this->response->setOutput( $xml->asXML() ); + } + } + ``` + + The response is: + ```xml + + + bla + foo + + ``` + + ## Set and load configurations + + The config library in Phacil Framework obtains values to use in entire web app of: `$config` array in *config.php* file, the settings table in your configured database or both. + + Is based in key->value mode with options like serialized value. + + To use a configuration value, just call `$this->config->get('nameOfKey')`. + + To storage a config value, use a SQL table called settings or an array. + + #### Sample of storage in array method + + ```php + $config = array( + "title" => "ExacTI Phacil Framework", + "config_error_display" => true, + "config_error_log" => false, + "phones" => array( + "BR" => '+55 11 4115-5161', + "USA" => '+1 (845) 579-0362') + ); + ``` + #### Sample of storage in SQL table + + | setting_id | group | key | value | serialized | + | ----- | ----- | ----- | ----- | ----- | + | 1 | | title | ExacTI Phacil Framework | 0 | + | 2 | config | config_error_display | 1 | 0 | + | 3 | config | config_error_log | 0 | 0| + | 4 | | phones | a:2:{s:2:"BR";s:16:"+55 11 4115-5161";s:3:"USA";s:17:"+1 (845) 579-0362";} | 1 | + + ### Reserved config keys + + This config keys are reserved to use for framework. + + * **config_error_log:** Define if errors, notices and warnings is stored in log file. *(boolean)* + * **config_error_display:** Indicates if PHP show the errors, notices and warnings in screen. *(boolean)* + * **config_template:** Folder template name. *(string)* + * **config_error_filename:** Log filename. *(string)* + * **config_seo_url:** Indicates if SEO URL routes is enabled. *(boolean)* + * **config_mail_protocol:** Define the method of mail library use to send e-mails (mail or smtp values only). *(string)* + * **config_compression:** Output gzip compression value [0-9]. *(integer)* + +### Recommended settings SQL Query + +#### MySQL/MariaDB sample + ```SQL + CREATE TABLE `settings` ( + `setting_id` int(11) NOT NULL AUTO_INCREMENT, + `group` varchar(32) COLLATE utf8_bin, + `key` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `value` mediumtext COLLATE utf8_bin, + `serialized` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`setting_id`) +) AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + ``` + #### MS SQL Server sample +```SQL + CREATE TABLE settings ( + [setting_id] int NOT NULL IDENTITY PRIMARY KEY, + [group] varchar(32), + [key] varchar(64) NOT NULL DEFAULT '', + [value] nvarchar(max), + [serialized] tinyint DEFAULT '0', + UNIQUE ([key]) +); + ``` + + ## Registrations + + If you need to register a class or other resource to use in your entire application, you can create using the file *registrations.php* in the **system** folder. + Use to specify a `$registry->set('name', $function);` with the function, class or method if you need. After this, just use `$this->name` to access this registry inside a controller or model. + + #### Sample to register a simple class + Declare in /system/registrations.php + ```php + set('cripto', $criptoClass); + ``` + + Using in /controler/demo/sample.php + ```php + cripto->function($value); + + echo $use; + } + } + ``` + + You can make registrations more complex too, for sample, use another database or a load call. + + Declare in /system/registrations.php + ```php + load->database($driver, $hostname, $username, $password, $database); + //see the load database method above for better comprehension + } + } + new DBne($registry); + ``` + + Using in /controller/demo/sample.php + ```php + class ControllerDemoSample extends Controller { + public function index() { + $this->test->query("SELECT * FROM mundo"); + } + } + ``` + + ***Pay attention:*** *Use the registrations only for objects that you need to access from the entire application. E.g.: If you need to connect a one more database but you just use for one model, it's better to load just inside the model, for awesome performance.* + + + ## License + + This project is maintained for [ExacTI IT Solutions](https://exacti.com.br) with GPLv3 license. diff --git a/system/database/autoload.php b/system/database/autoload.php index 5052e80..cd3e9de 100644 --- a/system/database/autoload.php +++ b/system/database/autoload.php @@ -2,8 +2,6 @@ define('DIR_DATABASE', (__DIR__)."/database/"); -//echo DIR_DATABASE; - include __DIR__."/library/db.php"; if(defined('DB_DRIVER')) { @@ -11,5 +9,5 @@ if(defined('DB_DRIVER')) { $db = new DB(DB_DRIVER, DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE); } else { global $db; - $db = false; + $db = new DB('nullStatement', NULL, NULL, NULL, NULL); } diff --git a/system/database/database/db_pdo.php b/system/database/database/db_pdo.php index 3a36a81..ac52a85 100644 --- a/system/database/database/db_pdo.php +++ b/system/database/database/db_pdo.php @@ -44,7 +44,7 @@ final class DB_PDO * @param string $name The database name * @param string $charset Encoding connection */ - public function __construct($host, $user, $pass, $name, $charset = 'utf8mb4') + public function __construct($host, $user, $pass, $name, $port = '3306', $charset = 'utf8mb4') { $this->params = new stdClass; # keep connection data @@ -53,7 +53,7 @@ final class DB_PDO $this->params->pass = $pass; $this->params->name = $name; $this->params->charset = $charset; - $this->params->connstr = "mysql:host={$host};dbname={$name};charset={$charset}"; + $this->params->connstr = "mysql:host={$host};port={$port};dbname={$name};charset={$charset}"; # add the connection parameters $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES '{$charset}'"; $this->connect(); diff --git a/system/database/database/dbmysqli.php b/system/database/database/dbmysqli.php index e15ee26..0754769 100644 --- a/system/database/database/dbmysqli.php +++ b/system/database/database/dbmysqli.php @@ -31,7 +31,7 @@ final class DBMySQLi { } } public function escape($value) { - return $this->`->real_escape_string($value); + return $this->connection->real_escape_string($value); } public function countAffected() { diff --git a/system/database/database/mpdo.php b/system/database/database/mpdo.php index 0bd1ab6..927e2c1 100644 --- a/system/database/database/mpdo.php +++ b/system/database/database/mpdo.php @@ -3,9 +3,9 @@ final class mPDO { private $connection = null; private $statement = null; - public function __construct($hostname, $username, $password, $database, $port = '3306') { + public function __construct($hostname, $username, $password, $database, $port = '3306', $charset = 'UTF8') { try { - $dsn = "mysql:host={$hostname};port={$port};dbname={$database};charset=UTF8"; + $dsn = "mysql:host={$hostname};port={$port};dbname={$database};charset={$charset}"; $options = array( \PDO::ATTR_PERSISTENT => true, \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION); diff --git a/system/database/database/mmsql.php b/system/database/database/mssql.php similarity index 93% rename from system/database/database/mmsql.php rename to system/database/database/mssql.php index acf0da0..4c83f7a 100644 --- a/system/database/database/mmsql.php +++ b/system/database/database/mssql.php @@ -2,7 +2,7 @@ final class MSSQL { private $connection; - public function __construct($hostname, $username, $password, $database) { + public function __construct($hostname, $username, $password, $database, $port = '1443', $charset = 'utf8') { if (!$this->connection = mssql_connect($hostname, $username, $password)) { exit('Error: Could not make a database connection using ' . $username . '@' . $hostname); } diff --git a/system/database/database/mysql.php b/system/database/database/mysql.php index bc4449f..55aa1fe 100644 --- a/system/database/database/mysql.php +++ b/system/database/database/mysql.php @@ -2,7 +2,7 @@ final class MySQL { private $connection; - public function __construct($hostname, $username, $password, $database, $charset = 'utf8mb4') { + public function __construct($hostname, $username, $password, $database, $port = '3306', $charset = 'utf8') { if (!$this->connection = mysql_connect($hostname, $username, $password)) { exit('Error: Could not make a database connection using ' . $username . '@' . $hostname); } diff --git a/system/database/database/nullStatement.php b/system/database/database/nullStatement.php new file mode 100644 index 0000000..4b85126 --- /dev/null +++ b/system/database/database/nullStatement.php @@ -0,0 +1,33 @@ +connection = NULL; + } + + public function query($sql) { + $result = new \stdClass(); + $result->num_rows = NULL; + $result->row = NULL; + $result->rows = NULL; + return $result; + } + + public function escape($value) { + return NULL; + } + + public function countAffected() { + return NULL; + } + + public function getLastId() { + return NULL; + } + + public function __destruct() { + return NULL; + } +} +?> \ No newline at end of file diff --git a/system/database/database/odbc.php b/system/database/database/odbc.php deleted file mode 100644 index e69de29..0000000 diff --git a/system/database/database/oracle.php b/system/database/database/oracle.php new file mode 100644 index 0000000..b53ddf9 --- /dev/null +++ b/system/database/database/oracle.php @@ -0,0 +1,54 @@ +connection = oci_connect($username, $password, $hostname."/".$database, $charset); + if (!$this->connection) { + $e = oci_error(); + $this->error = $e; + throw new \Exception(trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR)); + } + oci_set_client_info($this->connection, "Administrator"); + oci_set_module_name($this->connection, $module); + oci_set_client_identifier($this->connection, $cid); + + } + public function query($sql) { + $stid = oci_parse($this->connection, $sql); + oci_execute($stid); + if (!$this->connection) { + oci_fetch_all($stid, $res); + + $result = new \stdClass(); + $result->num_rows = oci_num_rows($stid); + $result->row = isset($res[0]) ? $res[0] : array(); + $result->rows = $res; + + return $result; + + + } else { + throw new \Exception('Error: ' . oci_error() . '
' . $sql); + } + } + public function escape($value) { + return str_replace("'", "", $value); + } + + public function countAffected() { + return NULL; + } + public function getLastId() { + return NULL; + } + + public function isConnected() { + return $this->connection; + } + + public function __destruct() { + oci_close($this->connection); + } +} \ No newline at end of file diff --git a/system/database/database/postgre.php b/system/database/database/postgre.php index 777788e..61d05ad 100644 --- a/system/database/database/postgre.php +++ b/system/database/database/postgre.php @@ -1,14 +1,14 @@ link = pg_connect('host=' . $hostname . ' port=' . $port . ' user=' . $username . ' password=' . $password . ' dbname=' . $database)) { throw new \Exception('Error: Could not make a database link using ' . $username . '@' . $hostname); } if (!pg_ping($this->link)) { throw new \Exception('Error: Could not connect to database ' . $database); } - pg_query($this->link, "SET CLIENT_ENCODING TO 'UTF8'"); + pg_query($this->link, "SET CLIENT_ENCODING TO '".$charset."'"); } public function query($sql) { $resource = pg_query($this->link, $sql); diff --git a/system/database/database/sqlite.php b/system/database/database/sqlite.php deleted file mode 100644 index e69de29..0000000 diff --git a/system/database/database/sqlsrv.php b/system/database/database/sqlsrv.php new file mode 100644 index 0000000..5e44ece --- /dev/null +++ b/system/database/database/sqlsrv.php @@ -0,0 +1,97 @@ + $username, + "PWD" => $password, + "Database" => $database + ); + + if (!$this->link = \sqlsrv_connect($hostname, $connectionInfo)) { + exit('Error: Could not make a database connection using ' . $username . '@' . $hostname); + } + + /* + if (!mssql_select_db($database, $this->link)) { + exit('Error: Could not connect to database ' . $database); + } + */ + + sqlsrv_query("SET NAMES 'utf8'", $this->link); + sqlsrv_query("SET CHARACTER SET utf8", $this->link); + } + + public function query($sql) { + $resource = \sqlsrv_query($sql, $this->link); + + if ($resource) { + if (is_resource($resource)) { + $i = 0; + + $data = array(); + + while ($result = \sqlsrv_fetch_array($resource, SQLSRV_FETCH_ASSOC)) { + $data[$i] = $result; + + $i++; + } + + \sqlsrv_free_stmt($resource); + + $query = new \stdClass(); + $query->row = isset($data[0]) ? $data[0] : array(); + $query->rows = $data; + $query->num_rows = $i; + + unset($data); + + return $query; + } else { + return true; + } + } else { + trigger_error('Error:
' . $sql); + exit(); + } + } + + public function escape($value) { + $unpacked = unpack('H*hex', $value); + + return '0x' . $unpacked['hex']; + } + + public function countAffected() { + return \sqlsrv_rows_affected($this->link); + } + + public function getLastId() { + $last_id = false; + + $resource = \sqlsrv_query("SELECT @@identity AS id", $this->link); + + if ($row = \sqlsrv_fetch($resource)) { + $last_id = trim($row[0]); + } + + sqlsrv_free_stmt($resource); + + return $last_id; + } + + public function __destruct() { + \sqlsrv_close($this->link); + } +} \ No newline at end of file diff --git a/system/database/database/sqlsrvpdo.php b/system/database/database/sqlsrvpdo.php new file mode 100644 index 0000000..3fdfe93 --- /dev/null +++ b/system/database/database/sqlsrvpdo.php @@ -0,0 +1,108 @@ +connection = new \PDO("sqlsrv:Server=" . $hostname . ";Database=" . $database, $username, $password, array(\PDO::SQLSRV_ATTR_DIRECT_QUERY => true)); + } catch(\PDOException $e) { + throw new \Exception('Failed to connect to database. Reason: \'' . $e->getMessage() . '\''); + } + + + } + + public function prepare($sql) { + $this->statement = $this->connection->prepare($sql); + } + + public function bindParam($parameter, $variable, $data_type = \PDO::PARAM_STR, $length = 0) { + if ($length) { + $this->statement->bindParam($parameter, $variable, $data_type, $length); + } else { + $this->statement->bindParam($parameter, $variable, $data_type); + } + } + + public function execute() { + try { + if ($this->statement && $this->statement->execute()) { + $data = array(); + + while ($row = $this->statement->fetch(\PDO::FETCH_ASSOC)) { + $data[] = $row; + } + + $result = new \stdClass(); + $result->row = (isset($data[0])) ? $data[0] : array(); + $result->rows = $data; + $result->num_rows = $this->statement->rowCount(); + } + } catch(\PDOException $e) { + throw new \Exception('Error: ' . $e->getMessage() . ' Error Code : ' . $e->getCode()); + } + } + + public function query($sql, $params = array()) { + $this->statement = $this->connection->prepare($sql); + + $result = false; + + try { + if ($this->statement && $this->statement->execute($params)) { + $data = array(); + + while ($row = $this->statement->fetch(\PDO::FETCH_ASSOC)) { + $data[] = $row; + } + + $result = new \stdClass(); + $result->row = (isset($data[0]) ? $data[0] : array()); + $result->rows = $data; + $result->num_rows = $this->statement->rowCount(); + } + } catch (\PDOException $e) { + throw new \Exception('Error: ' . $e->getMessage() . ' Error Code : ' . $e->getCode() . '
' . $sql); + } + + if ($result) { + return $result; + } else { + $result = new \stdClass(); + $result->row = array(); + $result->rows = array(); + $result->num_rows = 0; + return $result; + } + } + + public function escape($value) { + return str_replace(array("\\", "\0", "\n", "\r", "\x1a", "'", '"'), array("\\\\", "\\0", "\\n", "\\r", "\Z", "\'", '\"'), $value); + } + + public function countAffected() { + if ($this->statement) { + return $this->statement->rowCount(); + } else { + return 0; + } + } + + public function getLastId() { + return $this->connection->lastInsertId(); + } + + public function isConnected() { + if ($this->connection) { + return true; + } else { + return false; + } + } + + public function __destruct() { + $this->connection = null; + } +} \ No newline at end of file diff --git a/system/engine/loader.php b/system/engine/loader.php index 7c9a440..2afe93e 100644 --- a/system/engine/loader.php +++ b/system/engine/loader.php @@ -53,14 +53,14 @@ final class Loader { } } - public function database($driver, $hostname, $username, $password, $database, $prefix = NULL, $charset = 'UTF8') { - $file = DIR_SYSTEM . 'database/' . $driver . '.php'; - $class = 'Database' . preg_replace('/[^a-zA-Z0-9]/', '', $driver); + public function database($driver, $hostname, $username, $password, $database, $port = NULL, $charset = NULL) { + $file = DIR_SYSTEM . 'database/database/' . $driver . '.php'; + $class = ($driver); if (file_exists($file)) { include_once($file); - $this->registry->set(str_replace('/', '_', $driver), new $class()); + $this->registry->set(str_replace('/', '_', $database), new $class($hostname, $username, $password, $database)); } else { trigger_error('Error: Could not load database ' . $driver . '!'); exit(); diff --git a/system/registrations.php b/system/registrations.php index a1bc86e..bc69fb5 100644 --- a/system/registrations.php +++ b/system/registrations.php @@ -1,2 +1,2 @@ dispatch($action, new Action('error/not_found')); // Output $response->output(); -//var_dump(new ActionSystem('url/seo_url')); - ?> \ No newline at end of file diff --git a/system/url/seo_url.php b/system/url/seo_url.php index ae5b5c6..54d6ca9 100644 --- a/system/url/seo_url.php +++ b/system/url/seo_url.php @@ -1,5 +1,7 @@ config->get('config_seo_url')) { @@ -11,17 +13,13 @@ class SystemUrlSeoUrl extends Controller { //$parts = explode('/', $this->request->get['_route_']); $parts = array($this->request->get['_route_']); - if($this->db != false) { - - } foreach ($parts as $part) { - if($this->db != false) { - $query = $this->db->query("SELECT * FROM url_alias WHERE keyword = '" . $this->db->escape($part) . "'"); - } + if(defined('USE_DB_CONFIG') && USE_DB_CONFIG == true) + $query = $this->db->query("SELECT * FROM url_alias WHERE keyword = '" . $this->db->escape($part) . "'"); - if ($this->db != false && $query->num_rows === 1) { + if (isset($query) && $query != false && $query->num_rows === 1) { - $url = explode('=', $query->row['query']); + //$url = explode('=', $query->row['query']); if($query->row['get'] != "") { $a = explode(',', $query->row['get']); @@ -34,8 +32,16 @@ class SystemUrlSeoUrl extends Controller { //var_dump($query->row['query']); $this->request->get['route'] = $query->row['query']; - } else { - $this->request->get['route'] = 'error/not_found'; + } elseif (defined('ROUTES') && is_array(ROUTES)) { + $rotas = ROUTES; + if(isset($rotas[$part])){ + $this->request->get['route'] = $rotas[$part]; + } else { + $this->request->get['route'] = $this->notfound; + } + + } else { + $this->request->get['route'] = $this->notfound; } } @@ -55,16 +61,17 @@ class SystemUrlSeoUrl extends Controller { $data = array(); parse_str($url_data['query'], $data); - - //var_dump($data); - + foreach ($data as $key => $value) { - //var_dump($value); + if (isset($data['route'])) { - $query = $this->db->query("SELECT * FROM url_alias WHERE `query` = '" . $this->db->escape($value) . "'"); - if ($query->num_rows) { + if(defined('USE_DB_CONFIG') && USE_DB_CONFIG == true) + $query = $this->db->query("SELECT * FROM url_alias WHERE `query` = '" . $this->db->escape($value) . "'"); + if (isset($query) && $query->num_rows && $query->num_rows != NULL) { $url .= '/' . $query->row['keyword']; - } + } elseif (defined('ROUTES') && is_array(ROUTES)) { + $url .= '/' .(array_search($value, ROUTES)); + } unset($data[$key]); }