PHPackages                             polyfony-inc/polyfony - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. polyfony-inc/polyfony

ActiveProject[Framework](/categories/framework)

polyfony-inc/polyfony
=====================

Polyfony is a simple and powerfull PHP micro-framework

2.6.x-dev(5y ago)101791[53 issues](https://github.com/polyfony-inc/polyfony/issues)GPL-3.0-onlyPHPPHP &gt;=7.1.0

Since Mar 13Pushed 1y ago3 watchersCompare

[ Source](https://github.com/polyfony-inc/polyfony)[ Packagist](https://packagist.org/packages/polyfony-inc/polyfony)[ RSS](/packages/polyfony-inc-polyfony/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (6)Versions (3)Used By (0)

[![SensioLabsInsight](https://camo.githubusercontent.com/bf699299f0cd59b76d474ed19d7b89b5acb31d412cd15db7e0c845dc79a9ac26/68747470733a2f2f696e73696768742e73656e73696f6c6162732e636f6d2f70726f6a656374732f37313366613562652d623364362d346131302d623534342d3930656634353538306563302f6d696e692e706e67)](https://insight.sensiolabs.com/projects/713fa5be-b3d6-4a10-b544-90ef45580ec0) [![Maintainability](https://camo.githubusercontent.com/e323a604d6b549238554cd28560241a6288415fc72727e05519e9e25ad900937/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f64636238356630336432313838313435303461632f6d61696e7461696e6162696c697479)](https://codeclimate.com/github/polyfony-inc/polyfony/maintainability)

Polyfony is an intuitive, light and powerful PHP micro-framework.
-----------------------------------------------------------------

[](#polyfony-is-an-intuitive-light-and-powerful-php-micro-framework)

#### Philosophy

[](#philosophy)

Inspired by Symfony and Laravel but tailored to favour an inclination towards extreme simplicity and efficiency.
Compared to major PHP frameworks, Polyfony covers 95%+ of what we need most of the time, and does so using a fragment of the ressources, space, configuration files and dependencies required by major frameworks.
It features routing, bundles, controllers, profiler, views, ORM, tests, caches, locales, events, authentication, environments, form helper, CLI helper... and extensibility via composer.

#### Footprint [of an Hello World](https://github.com/polyfony-inc/polyfony/wiki/Benchmark)

[](#footprint-of-an-hello-world)

- ≤ 300 Ko of disk space *(35% of comment lines)*
- ≤ 400 Ko of RAM
- ≤ 2.5 ms (cold)

Requirements
------------

[](#requirements)

You need a POSIX compatible system (Linux/MacOS/xBSD), PHP &gt;= 7.4 with ext-pdo, ext-sqlite3, ext-mbstring, ext-msgpack and a rewrite module for your webserver.

Disclaimer
----------

[](#disclaimer)

If you are considering using this framework instead of a major and well supported framework such as Laravel, there is something very wrong with your decision making process and/or project assesment.

In almost every case, using mainstream framework would be a better choice.

Installation
------------

[](#installation)

###### To download &amp; preconfigure the framework in *your-project-folder*

[](#to-download--preconfigure-the-framework-in-your-project-folder)

```
composer create-project --stability=dev polyfony-inc/polyfony your-project-folder

```

*--stability=dev is mandatory since we don't publish releases yet*Pretty much all the dependencies that get installed by composer are only required by PHPUnit.

###### NginX [configuration](https://github.com/AnnoyingTechnology/nginx-configuration-template/blob/master/conf.d/services/polyfony2.conf)

[](#nginx-configuration)

```
root /var/www/your-project-folder/Public
location / {
	try_files $uri $uri/ /index.php?$query_string;
}

```

###### LigHTTPd configuration

[](#lighttpd-configuration)

```
server.document-root 	= "/var/www/your-project-folder/Public/"
url.rewrite-once 		= ("^(?!/Assets/).*" => "/?")

```

###### Apache configuration

[](#apache-configuration)

```
DocumentRoot "/var/www/your-project-folder/Public/"

```

Almost no learning required
---------------------------

[](#almost-no-learning-required)

This *readme.md* file should be enough to get you started, you can also browse the `Private/Bundles/Demo/` bundle and browse the Framework's source code. As the framework classes are static, everything is **always available, everywhere** thru simple and natural naming.

*The code bellow assumes you are prefixing the `Polyfony` namespace before each call.*

### [Request](https://github.com/polyfony-inc/polyfony/wiki/Reference#class-polyfonyrequest)

[](#request)

```
// retrieve an url parameter
Request::get('blog_post_id');

// retrieve a posted field named `search_expression`
Request::post('search_expression');

// retrieve a posted file
Request::files('attachment_document');

// retrieve a request header
Request::header('Accept-Encoding');

// retrieve the user agent
Request::server('HTTP_USER_AGENT');

// check if the method is post (returns a boolean)
Request::isPost();

// check if the request is done using ajax (returns a boolean)
Request::isAjax();

// check if the request is done thru TLS/SSL (returns a boolean)
Request::isSecure();

// check if the request is from the command line (returns a boolean)
Request::isCli();
```

### [Database](https://github.com/polyfony-inc/polyfony/wiki/Reference#class-polyfonyquery)

[](#database)

Polyfony provides self-aware entities, in a similar way to an *ActiveRecord* (RubyOnRails) or an *Eloquent* object.

Examples bellow assume a table named `Pages` exists in the database. The `Models\Pages` file has the following minimum amount of code.

```
namespace Models;
class Pages extends \Polyfony\Entity {}
```

```
// Retrieve a database entity by its ID, here, the id 67
$webpage = new Pages(67);

// Retrieve another database entity by its `url` column
$webpage = new Pages(['url'=>'/my-awesome-vegan-burger-recipe/']);

// Retrieve a single Entity by its ID and generate an input to change its title property, with a custom css class
// note that any html in the title field will be escaped in the  to prevent XSS
// returns
(new Pages(67))
	->input('title', ['class'=>'form-control']);

// Create an new page, populate and save it
(new Pages)
	->set([
		'url'				=> '/veganaise-c-est-comme-de-la-mayonnaise-mais-vegan/',
		'title'				=> 'I\'m running out of ideas...',
		'description'		=> 'Meta descriptions get less and less important with Google\'s newest algorithms',
		'creation_date'		=> '18/04/1995', // this gets converted to a unixepoch automagically
		'modification_date'	=> time(),
		'contents'			=> 'Meh...',
		'categories_array'	=> ['Cooking', 'Organic'], // this get's saved as json automagically
		'id_creator'		=> Security::getAccount()?->get('id') // assuming you are logged in
	])
	->save();

// Alternatively, you can also create an entity this way
Pages::create([
	'url'		=>'...',
	'title'		=>'...',
	// more columns and values...
]);

// Retrieve the `title` and `id` of 5 pages that have the `Organic` category,
// that have been modified in the last week,
// and that have been created by user's id 7.
$pages = Pages::_select(['title','id'])
	->whereContains(['categories_array'=>'Organic'])
	->whereGreaterThan('modification_date', time() - 7*24*3600)
	->where(['id_creator'=>7])
	->limitTo(0,5)
	->execute();
```

#### Parameters

[](#parameters)

```
->where()			// == $value
->whereNot()			//  $value
->whereBetween()		// BETWEEN $min_value AND $max_value
->whereMatch()			// MATCH column AGAINST $value
->whereContains()		// % $value %
->whereEndsWith()		// % $value
->whereStartsWith() 		// $value %
->whereNotEmpty() 		//  '' and NOT NULL
->whereEmpty() 			// '' or NULL
->whereNotNull() 		// NOT NULL
->whereNull() 			// NULL
->whereGreaterThan() 		// < $value
->whereLessThan() 		// > $value
->whereIn() 			// IN ( values )
->whereNotIn() 			// NOT IN ( values )
```

#### Options

[](#options)

```
->orderBy()				// associative array ('column'=>'ASC')
->limitTo()				// $start, $end
->groupBy()				// ?
->first()				// return the first Entity instead of an array of Entities
```

#### Magic columns

[](#magic-columns)

- Columns ending with `_date`, `_on`, `_at` will be converted from `DD/MM/YYYY` to a timestamp and vice-versa
- Columns ending with `_datetime` will be converted from `DD/MM/YYYY HH:mm` to a timestamp and vice-versa
- Columns ending with `_array` will be converted and stored as json, then restored to their original type
- Columns ending with `_size` will be converted from bytes to human readable size

SettersStored asGettersvar\_dump-&gt;set(\['creation\_date'=&gt;'01/01/2018'\])1514808000-&gt;get('creation\_date')string '01/01/2018'-&gt;set(\['creation\_at'=&gt;'01/01/2018'\])1514808000-&gt;get('creation\_at', true)string '1514808000'-&gt;set(\['creation\_on'=&gt;'1514808000'\])1514808000-&gt;get('creation\_on')string '01/01/2018'-&gt;set(\['creation\_datetime'=&gt;'1514808000'\])1514808000-&gt;get('creation\_datetime')string '01/01/2018 12:00'-&gt;set(\['products\_array'=&gt;\['apple','peach'\]\])\["apple","peach"\]-&gt;get('products\_array')array \['apple','peach'\]-&gt;set(\['picture\_size'=&gt;'24938'\])24938-&gt;get('picture\_size')string '24.4 Ko'-&gt;set(\['picture\_size'=&gt;'24938'\])24938-&gt;get('picture\_size',true)string '24938'You can easily add elements to the end of an `_array` column. Assuming you have a `Process` **object**/table, which has a `events_array` **attribute**/column.

```
// create a new Process object
(new Process)
	// push an event into the events_array object
	->push('events_array', [
		// this array is arbitrary, you are free to push anything into the column
		'date'			=>time(),
		'is_important'	=>false,
		'message'		=>'Something just happened !'
	])
	// your can also ommit the _array, the framework will find the right column
	->push('events', [
		// this array is arbitrary, you are free to push anything into the column
		'date'			=>time(),
		'is_important'	=>true,
		'message'		=>'Something dubious just occured !'
	])
	->save();
```

#### Entities accessors

[](#entities-accessors)

Entites have basic `->set([$column=>$value])` and `->get($column, $bymypass_html_entities_protection=false)` methods. In addition, there are

- `->oset($associative_array, $columns_to_actualy_set)` "OnlySet", certain columns
- `->lget($column)` "LocalizedGet", will attempt to use a locale for the returned value
- `->tget($column, $length)` "TruncatedGet", will truncate a returned value exceeding the length

#### XSS Protection

[](#xss-protection)

Invoking -&gt;get() on any other columns will automatically escape special html symbols using PHP's `FILTER_SANITIZE_FULL_SPECIAL_CHARS` as to prevent XSS attacks. In situation where you actually want the raw data from the database, add `true` as a second parameter as such `$object->get('column_name', true);` to retrieve the data "as is". Calling Format::htmlSafe() anywhere in your code will provide you with the same escaping features.

### Data validators

[](#data-validators)

**Data validation should be managed by the developer with `symfony/validator`, `respect/validation`, `wixel/gump`, or similar packages.**That being said, there is a very basic *(and optional)* built-in validator, to prevent corrupted data from entering the database while manipulating objects.

To enforce it, declare a `VALIDATORS` constant array in your model, each key being a column, and each value being a regex, an array of allowed values or a standard PHP filter name (ex. `FILTER_VALIDATE_IP`).

- Example

```
Models\Accounts extends Polyfony\Security\Accounts {
// Normal model classes extend Polyfony\Entity.
// Accounts extends an intermediate (but transparent) class that adds authentication logic.

	const IS_ENABLED = [
		0	=>'No',
		1	=>'Yes'
	];

	const VALIDATORS = [

		// using PHP's built in validators
		'login'					=>FILTER_VALIDATE_EMAIL,
		'last_login_origin'		=>FILTER_VALIDATE_IP,
		'last_failure_origin'	=>FILTER_VALIDATE_IP,

		// using arrays
		'is_enabled'=>self::IS_ENABLED,
		'id_level'	=>self::ID_LEVEL

	];
}
```

The validation occurs when `->set()` is invoked and will throw exceptions.

Note that you don't have to include `NULL` or `EMPTY` values in your validators to allow them. `NULL/NOT NULL` are to be configured in your database, so that the framework knows which column can, and cannot be null.

**Please be aware that doing a batch -&gt;update (aka : not using distinct objects/Entities) on a table will circumvent those validators. This will get fixed in a later version.**

### Data filtering

[](#data-filtering)

Data filtering and sanitizing can be used *in addition* or *instead* of data validators. While validators throw exception when invalid data is encountered, data filters will clean up the data, so that it matches the expected nature of said data.

To enforce data filtering, declare a `FILTERS` constant array in your model, each key being a column, and each value being a filter name, or an array of filters names that will be applied one after the other.

- Example

```
// an imaginary group model, that represent a group of people
Models\Groups extends Polyfony\Entity {

	const FILTERS = [
		// replaces , with a dot and removes everything except 0-9 + - .
		'group_monthly_allowance'	=> 'numeric',
		// trim spaces, removes any special chars and capitalize each words
		'group_name'				=> ['trim','text','ucwords'],
		// removes any special chars and capitalize each words
		'group_manager_name'		=> ['text','strtoupper'],
		// cleanup an email address
		'group_manager_email'		=> 'email'
	];

}
```

The filtering occurs when `->set()` is invoked, and after the validations (if any).

##### List of available filters

[](#list-of-available-filters)

Filter nameWhat that filter doesstrtoupperapplies mb\_strtoupper()strtolowerapplies mb\_strtolower()ucfirstapplies ucfirst()ucwordsapplies ucwords()trimapplies trim()numericreplaces coma with dot then applies FILTER\_SANITIZE\_NUMBER\_FLOATintegerapplies FILTER\_SANITIZE\_NUMBER\_INTphoneremoves anything but 0 to 9 the plus sign and parenthesisemailapplies FILTER\_SANITIZE\_EMAILtextreplaces ' with ’ then removes &lt; &gt; &amp; " \\ /nameremoves anything but letters, space and ’slugapplies Polyfony/Format::slug()length{4-4096}applies mb\_substr()capslock{30,50,70}applies ucfirst(mb\_strtolower()) if the uppercase ratio exceeds XX %The `capslock30`, `capslock50` and `capslock70` don't affect the data if it has a low enough uppercase ratio. This filter allows for nicer and cleaner databases, it has been designed for older people who enjoy the *FUCK YEAH CAPS LOCK !!* lifestyle.

**An added benefit of using model's filters is that your inputs and textarea automatically get the right html attributes and types.**

Check out the following Model, View and HTML output.

```
class User extends Polyfony\Entity {
	const FILTERS = [
		'user_login'=>['email','length128']
	];
}
```

```
