Introduction
Hello and welcome to the Official SPLIT PHP Framework Documentation!
The Purpose
"Knowing PHP should be enough!"
This framework is born from the perception that the current tools available in the marketplace for PHP developers are hyper-complex, heavy-weighted and old-styled. This is, I think, what is causing the exodus, by the new generations of web devs, to other languages and technologies, which makes sense, when you look from the perspective of a dev who learnt how to make web apps using the simple Python's Flask or the practical Node's ExpressJS, for examples, and suddenly comes face to face with these gigantic behemoths available as frameworks in PHP, with their huge learning curves and their tons of dependencies and vendors. When these new devs are saying "I don't like PHP" what they don't understand is that it is not PHP itself what scared them off.
SPLIT is not MVC
This framework is not (just one more) MVC (yes, finally!). It is built on top of SOA (Service Oriented Architecture), which makes it more reusable, straightforward, RESTful friendly and an easy-to-go for making micro-services systems. It is designed to be simple and lean, with a low learning curve. Its mission is to be a light tool with simplified interface. Knowledge required to understand and maintain an application written with this framework is basic PHP and OOP and the only dependency to run it is PHP itself.
See more at: https://github.com/gabriel-guelfi/splitphp
The Concept
Why "SPLIT"? Firstly because the word "split" is a reference to micro-services and split systems architecture (of course you can make monoliths with it, if that's your thing). Furthermore, it is an acronym for these 5 bound concepts which are the bases that this framework leans on: "Simplicity", "Purity", "Lightness", "Intuitiveness" and "Target Mindness".
Simplicity:
The good and old KISS principle. An engineer shall solve a problem using the simplest way possible. If there is a simple solution that works, this is the right solution! When it depends on tons of configurations and different files just to have an endpoint that prints out "Hello World!" on the screen, something is wrong.
Purity:
No tons of vendors and no new vendor-specific syntaxes, only the plain and good PHP and Object-Oriented Programming. A framework is intended to be a facilitator, a toolbox for a specific technology, so the dependencies of the framework, shall be, ideally, only the technology itself.
Lightness:
Related to the 2 concepts above, a fast and light software tool creates cheaper and better quality systems and avoids lots of headaches and money losses.
Intuitiveness:
A developer should only have difficulties learning how to use a lib or framework if this developer isn't acquainted with the very technology on which the lib or framework is based. Take the colossally successful example of JQuery. A dev who knows javascript, understands JQuery in a matter of hours. This is tightly related to the "Purity" concept. If a PHP senior has to practice for weeks before becoming really comfortable using a specific PHP framework or lib, again: something is definitely not right.
Target Mindness:
The framework exists as a facilitator and must respect the "Simplicity" and "Intuitiveness" concepts, so it allows the engineer to not have to worry about technical issues like: handling requests, basic security, installations, pre-configurations, handling responses, dealing with external connections, dealing with concurrent executions, data consistence, etc., and to be able to focus only on building the solutions that address the business issues for which the system is being created. This framework aims to provide an interface that is more direct and as straightforward as possible.
The Service Oriented Architecture
What is a Service?
The key concept of a service is reusability and it is basically an encapsulated piece of functionality, which is accessible from any part within the application. So all services are accessible to one another from within the system, but not directly from the client, who only have access to an API Layer, which acts as a "gatekeeper" to the application. The result is a pool of reusable services which can be accessed from everywhere inside the application, but with controlled external access.
How does it work in practice?
The SPLIT PHP Framework represents its API Layer as WebServices, where the applications's endpoints are defined. From within an endpoint the WebService can call services and/or respond to the client. In summary, to create an API using SPLIT PHP's SOA, the dev will create the services, which are classes that perform the actual operations, then register endpoints on a WebService. Simple as that!
Licensing
SPLIT PHP Framework is an OPEN SOURCE software under the MIT license
This framework is free and open source and is intended to always be. You can use and modify it as you wish, respecting just a few rules defined by the MIT open source license bellow:
MIT License
Copyright (c) 2024 SPLIT PHP Community
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Getting Started
In this section you will find a walkthrough to install, set up and run a SPLIT PHP application.
Installation
To install the framework is definitely simple. Actually there is no actual installation. It is a matter of unzip its source on the desired location. Here is 4 options that can be used to accomplish this.
Composer
solution is being studied at the moment. You'll probably see it here in a future release.
All the 4 options bellow presume you already have PHP 7+ installed on your device. If you don't know how to install it, visit the PHP Official Manual.
1. Download from direct link:
Download the source and extract the contents.
2. With Command Line (Linux):
-
Download the source:
wget https://github.com/gabriel-guelfi/splitphp/archive/refs/tags/v1.2.11.zip
-
Extract it:
unzip splitphp-1.2.11.zip -d path/to/extraction/destination
-d path/to/extraction/destination
is to indicate the location where you want the extracted folder to be. If you're already on it,
you can omit this part of the command.
3. With Command Line (Windows PowerShell):
-
Download the source:
Invoke-WebRequest https://github.com/gabriel-guelfi/splitphp/archive/refs/tags/v1.2.11.zip -O splitphp.zip
-
Extract it:
Expand-Archive -LiteralPath splitphp-1.2.11.zip -DestinationPath path\to\extraction\destination
-DestinationPath path\to\extraction\destination
is to indicate the location where you want the extracted folder to be.
4. With Command Line (MacOS):
-
Download the source:
curl -O https://github.com/gabriel-guelfi/splitphp/archive/refs/tags/v1.2.11.zip
-
Extract it:
unzip splitphp-1.2.11.zip -d path/to/extraction/destination
-d path/to/extraction/destination
is to indicate the location where you want the extracted folder to be. If you're already on it,
you can omit this part of the command.
Configuration
In SPLIT PHP there is a small bunch of settings, which are static information, that are available in the entire system and they're used to do many different things.
In the root directory ("/") of your project you will find a file named example.config.ini. In this file you can set up your application to do stuff like establish connection to a specific database, define what to show as the first page of your system and so on. In summary: it is used to configure the app.
In order to do it, rename example.config.ini file to "config.ini" and set the options according to your application needs.
The Available Options
Here you'll find a detailed explanation of what is each option and how to set it up:
Option | Value | Description | Default |
---|---|---|---|
DB_CONNECT |
"on" /
"off"
|
If set to "on" , tries to connect to a database and throws an exception if the connection fails.
If you haven't a database associated with the application, keep it "off" .
|
"off" |
DBHOST |
A string containing the URL to the database's host. | This option indicates the address of the host where the database is located. | "localhost" |
DBNAME |
A string containing the name of the database. | This option indicates what is the name of the database of the application, inside that host. | None |
DBUSER_MAIN |
A string containing the main user's username of the database. | The main user is used to perform all operations in the database, except read operations. This option is this user's username credential. | None |
DBPASS_MAIN |
A string containing the main user's password of the database. | The main user is used to perform all operations in the database, except read operations. This option is this user's password credential. | None |
DBUSER_READONLY |
A string containing the read-only user's username of the database. | The read-only user is used to perform only the read operations in the database. This option is this user's username credential. | None |
DBPASS_READONLY |
A string containing the read-only user's password of the database. | The read-only user is used to perform only the read operations in the database. This option is this user's password credential. | None |
DBTYPE * |
"mysql" /
"postgresql" /
"sqllite" /
"sqlserver" /
"oracle"
|
Indicates the type of the database. | "mysql" |
DB_TRANSACTIONAL |
"on" /
"off"
|
All write operations in the database are executed, by default, within transactions. This allows the system to automatically
undo (rollback) these operations if something wrong happens and the application throws an exception during runtime. This assures the consistence
of the data in the base. If, for some specific reason, you don't want these behavior, you can turn it off setting this option to "off" . Otherwise,
keep it "on" .
|
"on" |
DB_WORK_AROUND_FACTOR |
An integer value | When an attempt to connect or to execute an operation in the database fails, the system tries to redo it again. This option indicates how many times the system shall retry it before give up and throws an exception. | 5 |
CACHE_DB_METADATA |
"on" /
"off"
|
When performing operations in database, the system does some mapping on the entities and stores it as metadata. This options indicates whether or not these metadata should be cached in-file. | "on" |
APPLICATION_NAME |
A string containing the name of the application. | The name of the application, that will be available in the entire system to whatever purpose. | None |
DEFAULT_ROUTE |
A string with the default route. (Example: "/home/hello" )
|
This options indicates what route shall be executed when an end-user accesses the root URL. (Example: http://www.my-site.com/) | None |
DEFAULT_TIMEZONE |
A timezone formatted string. | This option sets the timezone of the entire system to one of the PHP's supported timezone strings. (PHP Supported Timezones) | None |
HANDLE_ERROR_TYPES |
PHP constants. | This tells to PHP what kinds of errors and warnings it shall capture and handle. Do not change the defaults unless you know exactly what you're doing. | E_ALL & ~E_NOTICE & ~E_USER_NOTICE |
APPLICATION_LOG |
"on" /
"off"
|
This indicates to the system whether or not it should save logs of the errors and exceptions at the application level. | "on" |
PRIVATE_KEY * |
A string containing a hash. | This hash is used as a secret to perform encryption/decryption operations, so do not share or publish this hash. | None |
PUBLIC_KEY * |
A string containing a hash. | This hash represents a key which can be used to identify the application or to perform encryption/decryption operations. | None |
NOTE
The DBTYPE
option currently supports only MySQL databases. So if you set any other value than "mysql"
on this option,
it won't work properly. The support to the other relational databases described, will be implemented in future releases.
NOTE
In future releases, the values of PRIVATE_KEY
and PUBLIC_KEY
options will be generated automatically. Today you can generate any hashes,
anyway you want, and then set'em on the options.
The config.ini file subsections
If you look into the config.ini file, you'll notice that it is divided by 4 subsections: [DATABASE]
, [SYSTEM]
, [VENDORS]
and [CUSTOM]
. The first 2 subsections, programmatically has no difference between them, all the options within them will be available throughout the system as
constants with the same option name and, in these cases, the division is only for a better readability. The latter are special subsections that work a little bit different.
> Subsection [CUSTOM]
:
The purpose of this subsection is for you to create your own constants and settings. So every option you set here, will be available as constants inside the system. An example:
In config.ini file:
[CUSTOM]
MY_NAME = "Aragorn Arathornson"
In any context inside the system:
$msg = "My name is ".MY_NAME; // The result of $msg will be "My name is Aragorn Arathornson"
OBS Don't put sensitive information in these [VENDORS]
or [CUSTOM]
subsections, as they aren't protected by being stored in Environment Variables.
NOTE
The [VENDORS]
subsection isn't functional, yet. A good interface for loading your external libs is being created for future releases. For now you can
make use of System::loadClass()
in any context to load your libs.
Configs are based on Environment Variables
For security reasons, all main options, i.e. inside [DATABASE]
and [SYSTEM]
subsections, are based on Environment Variables, which means that
the constants created in the system with these options's names are read from Environment Variables and if the system find a value already set in any of these Environment Variables,
it will keep the value stored in this variable and not use the one which is set on config.ini file. This is pretty much how the .env file, that can be found in other
frameworks and tools, works.
It also means that you can get rid of these options in the file, if you have them already set in the environment, or even get rid of the entire file, as well. As long as you have the Environment Variables set,
you will only need the file, if you have some extra configurations in the [VENDORS]
and/or [CUSTOM]
subsections. So, don't use sensitive data in [VENDORS]
or
[CUSTOM]
subsections, as it won't be protected by this Environment Variables mechanism.
Running The Application
Finally, let's put it to run!
The PHP's built-in web server
Nowadays PHP provides a built-in server for the developers, so they don't have to install and setup a web server system to run their PHP's apps locally, like it was in the days of old.
To start this PHP's server we use the command: php -S [hostname]:[port] -t [path/to/file.php]
, where [hostname]
is the name or address
of the host where you want to serve the application, [port]
is in which port, inside that host, the application will be available and
-t [path/to/file.php]
indicates the path to the PHP file that will be executed (i.e the application's entry point file) when a client access the URL
http://[hostname]:[port]
.
Run SPLIT PHP
To run an application written in SPLIT PHP, we use the PHP's built-in web server, just like the other frameworks also do, but here we do it using the PHP command explicitly*:
Inside the root directory of your project ("/"), open up a command line prompt or terminal and enter:
php -S localhost:8000 -t public
Done! Now go to your web browser and navigate to "http://localhost:8000" and you shall see the SPLIT PHP Framework's welcome page.
php console server:start
to start this server, without have to refer to the PHP's -S
command explicitly.
Components
In this section you will find detailed explanations for each of the main components you'll need in order to create and maintain an application with this framework.
Service
A Service is an independent piece of functionality, which is available all over the system. The keyword here is "reusability".
What is a Service?
A Service is basically a class, that you can name in any way you want, which extends the main class \engine\Service
, inheriting all its
mechanics, and located at "/application/services" folder.
It's in the services that the magic happens. All database operations, business rules, requirements, etc. goes in the services. We can say that the ensemble of the services, is the system itself. So, basically what you got is a system that is formed by an ensemble of classes. Simple enough?!
Let me show you an example of a Service:
Imagine that you're building a Service which performs basic arithmetic operations.
- Firstly, you create the Service itself:
<?php // this class is located in a file called "arithmetic.php" inside the folder "/application/services". namespace application\services; use \engine\Service; // this is the main class that this service will inherit from. class Arithmetic extends Service{ }
- Now, let's add the sum functionality of our Arithmetic's Service:
<?php // This class is located in a file called "arithmetic.php" inside the folder "/application/services". namespace application\services; use \engine\Service; // this is the main class that this service will inherit from. class User extends Service{ public function sum($value1, $value2){ return $value1 + $value2; } }
- If you get the idea, let's add subtraction, multiplication and division operations in our service:
<?php // This class is located in a file called "arithmetic.php" inside the folder "/application/services". namespace application\services; use \engine\Service; // this is the main class that this service will inherit from. class User extends Service{ public function sum($value1, $value2){ return $value1 + $value2; } public function subtraction($value1, $value2){ return $value1 - $value2; } public function multiplication($value1, $value2){ return $value1 * $value2; } public function division($value1, $value2){ return $value1 / $value2; } }
Ok, done! But how can you access these functionalities from outside this Service?
Inside any service you have the $this->getService()
method available. This method is used to invoke and instantiate any other service. So, from a service,
you can invoke another service by using $this->getService()
builtin method.
The $this->getService()
method requires the service's path as a parameter - this path starting from the "/application/services"
folder - to identify which service it will return and the returned value is an object of the service's class itself. This allows you to call service's operations using
builder design pattern, directly, without having
to instantiate the service's object explicitly:
<?php
...
$this->getService('path/to/the/service')->serviceMethod();
...
Here's an example:
Imagine, now, that in your system you have a "Salesorder" service which has an applyDiscount operation, which uses the operations defined in that
Arithmetic's service, that we created before, to calculate the discounts of a sales order. In order to do it, it will make use of $this->getService()
method:
<?php
namespace application\service;
use \engine\Service;
class Salesorder extends Service{
public function applyDiscount($orderValue, $discountValue){
$finalValue = $this->getService('arithmetic')->subtraction($orderValue, $discountValue);
return $finalValue;
}
}
$this->getService()
in the session
Reference Guide of this documentation.
What's more in it?
Ok, so now we know that a service is just a class stored in "/application/services" folder, which extends the \engine\Service
framework's
class and, also, that we can, from any service, access any method of any other service, using the builtin method $this->getService()
. But what more
can a service do?
The answer is: manage Templates!
A template is a PHP file, which contains HTML (or any other markup language that you want) and it must be located in "/application/templates". The service does
so by using another builtin method called $this->renderTemplate()
. This method receives a template's path as the first parameter - this path starting from
"/application/templates" folder - and returns a string of the contents of this template, which you can use to render as page in the browser or use as a message's
body in an email, for instance:
-
The template located at "/application/templates/hello.php":
<h1>Hello World from a template!</h1>
-
The service that manages it:
<?php namespace \application\services; use \engine\Service; class Servicename extends Service{ public function printTemplateHello(){ $tplContent = $this->renderTemplate('hello'); // the variable $tplContent holds a string with the content "<h1>Hello World from a template!</h1>" echo $tplContent; // prints out the HTML recovered from the template. } }
You can also pass, as the second parameter, a var list, with dynamic content, to this template, so it can render it's value inside the HTML content:
-
The template located at "/application/templates/hello.php":
<h1>Hello <?php echo $name; ?>!</h1>
-
In the service:
<?php namespace \application\services; use \engine\Service; class Servicename extends Service{ public function printTemplateHello(){ $tplContent = $this->renderTemplate('hello', ['name' => "Gandalf"]); // the variable $tplContent holds a string with the content "<h1>Hello Gandalf!</h1>" echo $tplContent; // prints out the HTML recovered from the template. } }
$this->renderTemplate()
in the service.
$this->renderTemplate()
in the session
Reference Guide of this documentation.
Wrapping up!
- A service is a class, located at "/application/services" folder, that manages a single part of the system (Ex.: User service, which manages application's users).
-
It is available to any other service in the application by the use of
$this->getService()
method, but it's not directly available to the client. - All logics, rules and operations of the system will be performed by services.
-
The
$this->getService()
method exposes a builder design pattern interface, so you can call the service's method right after$this->getService()
, without having to instantiate the service's class object explicitly. - It's in the service that you will operate your application's database, using the interface of DAO.
-
You can manage your application's templates by the use of
$this->renderTemplate()
method.
That's it! In the next section you will learn about the Web Service and how you connect your services to the requests of the client. See you there!
Web Service
The WebService is your API layer, the gatekeeper of your application.
How to create a WebService
Web Services are classes, which extend the \engine\WebService
main class, inheriting, though, all it's mechanics. Your application's web services,
are gonna be located under "/application/routes".
So, answering the question "How do I create a WebService in my app?": it's just a matter of creating a class, which extends \engine\WebService
class,
and save it in a file under the directory "/application/routes".
Routes and the URL
An application exposes its resources by the use of endpoints, which are basically routes, on which a function is executed, when accessed.
The anatomy of an endpoint
An application exposes its resources by the use of endpoints, which are basically routes, on which a function is executed, when accessed.
The Response object
An application exposes its resources by the use of endpoints, which are basically routes, on which a function is executed, when accessed.
WebService, Son of Service
An application exposes its resources by the use of endpoints, which are basically routes, on which a function is executed, when accessed.
Data Access Object (DAO)
This is the main way to access and operate data in the database. You don't need to setup persistence's data classes, models, etc. The connection to the database is already established and it uses the credentials specified in the config.ini file, all the setup is already made and it's already good to go. You can just invoke the DAO's methods directly in your services and eureka!
How does a data retrieving looks like using this Data Access Object?
<?php
$results = $this->getTable('YOUR_TABLE_NAME')
->filter('id')->equalsTo($someValue)
->find("SELECT some_column FROM `YOUR_TABLE_NAME` WHERE id = ?id?");
This code above retrieves data from a table named "YOUR_TABLE_NAME", using the query passed as argument to method find()
and stores the retrieved data as an array
into variable $results
. Furthermore, it replaces the "?id?" in the query string with the value in $someValue
variable. Note that the text between
the "??" is the same string passed as argument to filter()
method.
Let me show you an example:
Imagine that you have a table of PEOPLE and you want to build a method, in your People's Service, that retrieves a list of people's name and age, filtered by genre:
<?php
// Inside People's Service
public function listByGenre($genre){
$results = $this->getTable('PEOPLE')
->filter('genreParam')->equalsTo($genre) // Passing the value received by parameter.
->find("SELECT name, age FROM `PEOPLE` WHERE genre = ?genreParam?");
return $results; // This function is returning an array containing a list of people, filtered by the genre received in parameter $genre.
}
filter()->equalsTo()
serves the purpose of avoiding SQL Injection attacks. Of course you could just concat $someValue
into the query string directly,
but this would open your system to these kind of hacking. To ensure the security, always use filter()->equalsTo()
to pass dynamic values to your SQL queries.
Let's take a look into the anatomy of a DAO invoking:
-
Setting-up and retrieving DAO:
<?php $dao = $this->getTable('YOUR_TABLE_NAME') // It says to DAO in which table it is going to operate and returns the DAO itself.
-
Applying filters to the operation:
<?php $dao->filter('fieldName') // It says to DAO in which table's field it is going to be applied a value and returns the DAO again. ->equalsTo($someValue)// Passes along the value to be applied in the filter that have just been created.
-
Executing the operation and returning results:
<?php $dao->find($stringContainingSQL) // You can define the SQL that will be executed in here. It returns the results of the executed SQL.
Further options:
> The available operations:
Of course, besides the reading operation, which uses the find()
method, you can also perform Insert, Update and Delete:
-
Insert:
<?php // Define the data that will be inserted in the new record. $data = [ 'field1' => $value1, 'field2' => $value2, 'field3' => $value3, ]; /* * Insert a new record in table YOUR_TABLE_NAME, using the data passed in $data, * then returns an object containing the new record data. */ $newRecord = $this->getTable('YOUR_TABLE_NAME')->insert($data);
-
Update: pretty much the same of insert, but here you can filter the records which will be affected by the update:
<?php // Defining the "field => data" to be updated: $data = [ 'field1' => $value1, 'field2' => $value2, 'field3' => $value3, ]; // Updating records, filtered by ID. This returns the number of affected rows: $affectedRows = $this->getTable('YOUR_TABLE_NAME') ->filter('id')->equalsTo($id) // This $id var, can be a parameter of the service's function, for example. ->update($data);
-
Delete: It's just a matter of defining the filters of the deletion and invoking the function without arguments:
<?php // Delete records, filtered by ID. This also returns the number of affected rows: $affectedRows = $this->getTable('YOUR_TABLE_NAME') ->filter('id')->equalsTo($id) // This $id var, can be a parameter of the service's function, for example. ->delete();
> The available filter comparison options:
Sometimes we do not want our filter to be equals to the value passed. We may want to perform
a search filtered by values that are lesser than or bigger than the value we passed along to
the query. For example: imagine that you want delete all old records from a table, that were created before
a certain date. Instead of using ->equalsto()
method, you'll use lesserThan()
:
<?php
// This deletes all records that were created before january 1, 2022, then returns the number of the deleted records:
$deletedRecords = $this->getTable("TABLE_NAME")
->filter('date_created')->lesserThan('2022-01-01')
->delete();
Here's a list of all the available comparison methods:
Method | Ref. Operator | Use case |
---|---|---|
equalsTo() | = |
|
differentFrom() | != |
|
biggerThan() | > |
|
lesserThan() | < |
|
biggerOrEqualsTo() | >= |
|
lesserOrEqualsTo() | <= |
|
likeOf() | LIKE |
|
> The available filter logical options:
If we want to stack conditions on our filters, we have to make use of logical operators
("AND" and "OR") in our queries, right?! To do it with this DAO, we betake
te methods and()
and or()
(what a surprise! xD).
For example, imagine that you have a table which contains a list of students of all grades and you want to label those of the 7th grade, who obtain the better scores in the tests(9 points or more), with "better of the 7th grade". So you need to perform an update, filtering by the grade and by the scores:
<?php
$this->getTable("students")
->filter('grade')->equalsTo("7th")
->and('score')->biggerOrEqualsTo(9)
->update([
'label' => "better of the 7th grade"
]);
Now an example using or()
: Imagine that you want to delete all the products of 2 different categories.
This is how you do it:
<?php
$this->getTable("products")
->filter('category')->equalsTo("category_1")
->or('category')->equalsTo("category_2")
->delete();
find()
,
first()
or fetch()
, if you're passing a query to it, you'll have to specify the comparison and logical
operators explicitly in the query, so the use of these different methods will have no different effect.
Deploy In Production
Put it out to the world!
As it was pointed out in the Running Application section of this documentation, one should, instead of using the PHP's built-in web server to deploy applications on production environments, use a web server application as Nginx or Apache 2. Here it is explained how to deploy your SPLIT PHP app using these two, which are the main solutions fot this in the current marketplace.
Using NGINX Web Server
This tutorial assumes that you already have some knowledge on NGINX and Server Block files.
To configure Nginx to serve a SPLIT PHP application you need to:
- create a new Server Block file inside the sites-available Nginx's directory
- insert the code shown bellow, making the necessary adaptations, into this file
- save it and then enable it on Nginx
<?php
server {
listen 80;
listen [::]:80 ipv6only=on;
server_name example.com.br;
root /path/to/your/app/root/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
fastcgi_param QUERY_STRING $query_string;
}
location /resources {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri /index.php =404;
# This line below is a default path to PHP in linux systems. If your PHP is installed in another location, change it to your actual fpm path.
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ \.git {
deny all;
}
}
Here we have some points of attention:
- The
server_name
option: you must set this option with your application's domain name. - The
root
option: you must set this option with the absolute path to your application's public directory. - The
fastcgi_pass
option, insidelocation ~ \.php$
section: this option indicates the PHP fpm path inside your server. If you have an UNIX based OS installed on it, you probably won't have to change it. But if your server's PHP fpm is located elsewhere you will have to set the absolute path to it in here.
Now you just have to enable the newly created site's Server Block config file, and restart the NGINX web server.
Using Apache 2 Web Server
This tutorial assumes that you already have some knowledge on Apache 2 and its Virual Hosts.
To configure Apache to serve a SPLIT PHP application you need to:
- create a new Virual Host file inside the sites-available Apache's directory
- insert the code shown bellow, making the necessary adaptations, into this file
- save it and then enable it on Apache
<?php
<VirtualHost *:80>
ServerAdmin your.email@site-domain
ServerName site-domain.com
ServerAlias www.site-domain.com
DocumentRoot /path/to/application/root/public
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Here we have some points of attention:
- The
ServerAdmin
option: you must set this option with a proper email, where the site's administrator can receive messages. - The
ServerName
option: you must set this option with your application's domain name. - The
ServerAlias
option: you must set this option with your application's domain name preceding by "www.". - The
DocumentRoot
option: you must set this option with the absolute path to your application's public directory.
Now you just have to enable the newly created site's Virtual Host config file, and restart the Apache web server.
It is possible that the Apache's mod_rewrite
module isn't activated, then you will run into some problems with URL. In order to activate it manually
run this command:
<?php
a2enmod rewrite
*: This command can vary a little bit depending on your OS.
FAQ
Here's a list of frequently asked questions (FAQs) and answers on particular topics.
You can put all static files as CSS, Javascript, media and font files in "/public" directory. Then you can refer to these files
by indicating their URLs starting from "/", which is a reference to /public.
See, below an example of an image file placed at "/public/resources/img/example.jpg":
<img src="/resources/img/example.jpg"/>
For a better organization SPLIT PHP already has, inside /public, a "resources" folder, which is a preset that you can use to put your static files.
But keep in mind that you have total freedom to create your own directory/file tree inside /public, to make it the way you want.
When executing queries to read data from the database, you can make use of Dao::find()
passing to it a string containing the SQL Command, like this:
$this->getTable("TABLE_NAME")
->find("SELECT * FROM `TABLE_NAME`");
But what if you're writing a complex query with multiple lines? It would work perfectly, but your code's readability could be compromised. In this "/application/sql" folder
you can store your .sql
files, then pass its path to Dao::find()
:
$this->getTable("TABLE_NAME")
->find("foo/bar");
Changelog
See what's new added, changed, fixed or updated in the latest versions.
For Future Updates Follow SPLIT PHP on Github
Version 1.2 (10 May, 2022)
This release 1.2.11 has improvements in security, error handling and a major improvement on data access and general flexibility, besides some bug fixes.
Now it became more conceptual than ever, so I decided to change its name and image, from "DynamoPHP" to "SPLIT PHP", to better represent the fundamentals of the tool.
- Added DynamoPHP is now SPLIT PHP. See the section The Concept to understand why.
- Added New
setHeader()
andgetHeaders()
methods inResponse
object. - Added Added an additional field
$info
onSystem::errorLog()
method. - Added Database errors handled with specific Exception object which stores the problematic SQL Command.
- Added The application level errors logging now can be turned
on
/off
in the configurations. - Added A method
Service::setTemplateRoot()
to set a root path within /application/templates directory. - Added Standard Anti-XSRF validation for requests.
- Added New
Dao::first()
andDao::fetch()
wrapper methods. - Added New
Dao::bindParams()
method, that automatically parameterizes queries to the database. - Added New
Service::init()
method, for your service's custom initializations. - Added Added a
DEFAULT_TIMEZONE
config option. - Changed Removed too much specific functionalities from the
Utils
helper class. - Changed Automatic database connection closing assurance.
- Changed It is now possible to execute multiple
DAO
nested calls at once. - Changed Database read operations are now separated from the others in a specific read-only connection.
- Changed
RestServices
now possess a ready-to-useResponse
object at$this->response
. - Changed The
RestService::addEndpoint()
method now allows closure functions to be passed as handler function. - Changed Removed all vendor dependencies from the entire framework.
- Changed Restructured the entire framework in namespaces.
- Changed Removed
DEFAULT_RESTSERVICE
option from configurations. - Changed The
DEFAULT_ROUTE
config option now expects the full route. - Changed For better security, all configurations are now based on environment variables.
- Changed Improved CORS Policy to meet the compliance.
- Fixed Confusion and unpredictability on Internal Server Error status code
- Fixed Problem to parse requests containing query strings with encoded chars.
- Fixed Added a default logical operator
"and"
to database operations parameterized withDao::bindParams()
. - Fixed Fixed liability with constant
URL_APPLICATION
. - Fixed Fixed issues with error reporting.
- Fixed Replace linebreak method with
PHP_EOL
constant. - Fixed Fixed
null
issue with some outputs sanitized byhtmlspecialchars()
. - Fixed Fixed a deprecation warning for using
explode()
on a possiblynull
value. - Fixed An issue of log entry duplication in index.php.
- Added Standard .gitignore file.
- Added Automatic transactional database operations.
- Added Method
Service::requestURL()
to perform cURL requests. - Changed
Dao::find()
accepts no parameters for default queries. - Changed Improved user readable errors handling.
- Fixed Removed references to legacy debugger "Pesticide"
- Fixed Fixed a bug which was causing
POST
andPUT
to not receive query strings params. - Fixed
RestService
executingDAO
operations was throwing exception.
I think there are changes and features here to even release a major version, but as I choose to change the framework's name, I decided to maintain it in version 1.
Version 1.1 (30 Aug, 2021)
This release 1.1.4 has some improvements with database operations, included a feature to perform CURLs easier and have some fixes. See the list below for further details.
This release was intended to create features that make the developer's life easier, also improved the consistence of the resulting applications and, of course, fixed some bugs. Hope you enjoy!
Version 1.0 (7 July, 2021)
Initial Release