├── application ├── views │ ├── _templates │ │ ├── footer.php │ │ └── header.php │ ├── home │ │ ├── index.php │ │ ├── example_one.php │ │ └── example_two.php │ └── songs │ │ └── index.php ├── _install │ ├── 01-create-database.sql │ ├── 02-create-table-song.sql │ └── 03-insert-demo-data-into-table-song.sql ├── .htaccess ├── models │ ├── statsmodel.php │ └── songsmodel.php ├── config │ └── config.php ├── libs │ ├── controller.php │ └── application.php └── controller │ ├── home.php │ └── songs.php ├── public ├── img │ └── demo-image.png ├── js │ └── application.js └── css │ └── style.css ├── _tutorial ├── tutorial-part-01.png ├── tutorial-part-02.png ├── tutorial-part-03.png ├── tutorial-part-04.png └── tutorial-part-05.png ├── CHANGELOG.md ├── index.php ├── composer.json ├── .htaccess └── README.md /application/views/_templates/footer.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /application/_install/01-create-database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `php-mvc`; 2 | -------------------------------------------------------------------------------- /application/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Order Deny,Allow 3 | Deny from all 4 | 5 | -------------------------------------------------------------------------------- /public/img/demo-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinisaac/php-mvc/HEAD/public/img/demo-image.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinisaac/php-mvc/HEAD/_tutorial/tutorial-part-01.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinisaac/php-mvc/HEAD/_tutorial/tutorial-part-02.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinisaac/php-mvc/HEAD/_tutorial/tutorial-part-03.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinisaac/php-mvc/HEAD/_tutorial/tutorial-part-04.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinisaac/php-mvc/HEAD/_tutorial/tutorial-part-05.png -------------------------------------------------------------------------------- /application/views/home/index.php: -------------------------------------------------------------------------------- 1 |
2 |

You are in the View: application/views/home/index.php (everything in the box comes from this file)

3 |

In a real application this could be the homepage.

4 |
5 | -------------------------------------------------------------------------------- /application/views/home/example_one.php: -------------------------------------------------------------------------------- 1 |
2 |

You are in the View: application/views/home/example_one.php (everything in the box comes from this file)

3 |

In a real application this could be a normal page.

4 |
5 | -------------------------------------------------------------------------------- /application/views/home/example_two.php: -------------------------------------------------------------------------------- 1 |
2 |

You are in the View: application/views/home/example_two.php (everything in the box comes from this file)

3 |

In a real application this could be a normal page.

4 |
5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGE LOG 2 | ========== 3 | 4 | **January 4th 2014** 5 | - fixed htaccess issue when there's a controller named "index" and a base index.php (which collide) 6 | 7 | **December 29th 2013** 8 | - fixed case-sensitive model file loading (thanks "grrnikos") 9 | -------------------------------------------------------------------------------- /application/_install/02-create-table-song.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `php-mvc`.`song` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `artist` text COLLATE utf8_unicode_ci NOT NULL, 4 | `track` text COLLATE utf8_unicode_ci NOT NULL, 5 | `link` text COLLATE utf8_unicode_ci, 6 | PRIMARY KEY (`id`), 7 | UNIQUE KEY `id` (`id`) 8 | ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 9 | -------------------------------------------------------------------------------- /public/js/application.js: -------------------------------------------------------------------------------- 1 | // common way to initialize jQuery 2 | $(function() { 3 | // simple demo to show create something via javascript on the page 4 | if ($('#javascript-header-demo-box').length != 0) { 5 | $('#javascript-header-demo-box').hide(); 6 | $('#javascript-header-demo-box').text('Hello from JavaScript! This line has been added by public/js/application.js'); 7 | $('#javascript-header-demo-box').css('color', 'green'); 8 | $('#javascript-header-demo-box').fadeIn('slow'); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | /* Demo CSS */ 2 | 3 | html, body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | body { 9 | font-size: 15px; 10 | } 11 | 12 | table { 13 | font-size: 13px; 14 | } 15 | 16 | .container { 17 | border: 3px solid #000; 18 | padding: 20px; 19 | margin: 10px; 20 | } 21 | 22 | .where-are-we-box { 23 | position: relative; 24 | left: -20px; 25 | top: -20px; 26 | background-color: #bbb; 27 | padding: 10px 15px; 28 | display: inline-block; 29 | } 30 | 31 | .bold { 32 | font-weight: bold; 33 | } 34 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | =5.3.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | # Necessary to prevent problems when using a controller named "index" and having a root index.php 2 | # more here: http://httpd.apache.org/docs/2.2/content-negotiation.html 3 | Options -MultiViews 4 | 5 | # Activates URL rewriting (like myproject.com/controller/action/1/2/3) 6 | RewriteEngine On 7 | 8 | # Disallows others to look directly into /public/ folder 9 | Options -Indexes 10 | 11 | # When using the script within a sub-folder, put this path here, like /mysubfolder/ 12 | # If your app is in the root of your web folder, then leave it commented out 13 | RewriteBase /php-mvc/ 14 | 15 | # General rewrite rules 16 | RewriteCond %{REQUEST_FILENAME} !-d 17 | RewriteCond %{REQUEST_FILENAME} !-f 18 | RewriteCond %{REQUEST_FILENAME} !-l 19 | 20 | RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 21 | -------------------------------------------------------------------------------- /application/models/statsmodel.php: -------------------------------------------------------------------------------- 1 | db = $db; 12 | } catch (PDOException $e) { 13 | exit('Database connection could not be established.'); 14 | } 15 | } 16 | 17 | /** 18 | * Get simple "stats". This is just a simple demo to show 19 | * how to use more than one model in a controller (see application/controller/songs.php for more) 20 | */ 21 | public function getAmountOfSongs() 22 | { 23 | $sql = "SELECT COUNT(id) AS amount_of_songs FROM song"; 24 | $query = $this->db->prepare($sql); 25 | $query->execute(); 26 | 27 | // fetchAll() is the PDO method that gets all result rows 28 | return $query->fetch()->amount_of_songs; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /application/config/config.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PHP MVC skeleton 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 | Everything in this box is loaded from application/views/_templates/header.php ! 22 |
23 | The green line is added via JavaScript (to show how to integrate JavaScript). 24 |
25 |

The header (used on all pages)

26 | 27 |

Demo image, to show usage of public/img folder

28 |
29 | 30 |
31 | 32 |

Demo Navigation

33 | 43 | 44 |

Demo JavaScript

45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /application/views/songs/index.php: -------------------------------------------------------------------------------- 1 |
2 |

You are in the View: application/views/song/index.php (everything in this box comes from that file)

3 | 4 |
5 |

Add a song

6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |

Amount of songs (data from second model)

19 |
20 | 21 |
22 |

List of songs (data from first model)

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 |
IdArtistTrackLinkDELETE
id)) echo $song->id; ?>artist)) echo $song->artist; ?>track)) echo $song->track; ?> 40 | link)) { ?> 41 | link; ?> 42 | 43 | x
49 |
50 |
51 | -------------------------------------------------------------------------------- /application/models/songsmodel.php: -------------------------------------------------------------------------------- 1 | db = $db; 12 | } catch (PDOException $e) { 13 | exit('Database connection could not be established.'); 14 | } 15 | } 16 | 17 | /** 18 | * Get all songs from database 19 | */ 20 | public function getAllSongs() 21 | { 22 | $sql = "SELECT id, artist, track, link FROM song"; 23 | $query = $this->db->prepare($sql); 24 | $query->execute(); 25 | 26 | // fetchAll() is the PDO method that gets all result rows, here in object-style because we defined this in 27 | // libs/controller.php! If you prefer to get an associative array as the result, then do 28 | // $query->fetchAll(PDO::FETCH_ASSOC); or change libs/controller.php's PDO options to 29 | // $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ... 30 | return $query->fetchAll(); 31 | } 32 | 33 | /** 34 | * Add a song to database 35 | * @param string $artist Artist 36 | * @param string $track Track 37 | * @param string $link Link 38 | */ 39 | public function addSong($artist, $track, $link) 40 | { 41 | // clean the input from javascript code for example 42 | $artist = strip_tags($artist); 43 | $track = strip_tags($track); 44 | $link = strip_tags($link); 45 | 46 | $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)"; 47 | $query = $this->db->prepare($sql); 48 | $query->execute(array(':artist' => $artist, ':track' => $track, ':link' => $link)); 49 | } 50 | 51 | /** 52 | * Delete a song in the database 53 | * Please note: this is just an example! In a real application you would not simply let everybody 54 | * add/update/delete stuff! 55 | * @param int $song_id Id of song 56 | */ 57 | public function deleteSong($song_id) 58 | { 59 | $sql = "DELETE FROM song WHERE id = :song_id"; 60 | $query = $this->db->prepare($sql); 61 | $query->execute(array(':song_id' => $song_id)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /application/libs/controller.php: -------------------------------------------------------------------------------- 1 | openDatabaseConnection(); 20 | } 21 | 22 | /** 23 | * Open the database connection with the credentials from application/config/config.php 24 | */ 25 | private function openDatabaseConnection() 26 | { 27 | // set the (optional) options of the PDO connection. in this case, we set the fetch mode to 28 | // "objects", which means all results will be objects, like this: $result->user_name ! 29 | // For example, fetch mode FETCH_ASSOC would return results like this: $result["user_name] ! 30 | // @see http://www.php.net/manual/en/pdostatement.fetch.php 31 | $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING); 32 | 33 | // generate a database connection, using the PDO connector 34 | // @see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/ 35 | $this->db = new PDO(DB_TYPE . ':host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS, $options); 36 | } 37 | 38 | /** 39 | * Load the model with the given name. 40 | * loadModel("SongModel") would include models/songmodel.php and create the object in the controller, like this: 41 | * $songs_model = $this->loadModel('SongsModel'); 42 | * Note that the model class name is written in "CamelCase", the model's filename is the same in lowercase letters 43 | * @param string $model_name The name of the model 44 | * @return object model 45 | */ 46 | public function loadModel($model_name) 47 | { 48 | require 'application/models/' . strtolower($model_name) . '.php'; 49 | // return new model (and pass the database connection to the model) 50 | return new $model_name($this->db); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /application/controller/home.php: -------------------------------------------------------------------------------- 1 | loadModel('SongsModel'); 26 | $songs = $songs_model->getAllSongs(); 27 | 28 | // load another model, perform an action, pass the returned data to a variable 29 | // NOTE: please write the name of the model "LikeThis" 30 | $stats_model = $this->loadModel('StatsModel'); 31 | $amount_of_songs = $stats_model->getAmountOfSongs(); 32 | 33 | // load views. within the views we can echo out $songs and $amount_of_songs easily 34 | require 'application/views/_templates/header.php'; 35 | require 'application/views/songs/index.php'; 36 | require 'application/views/_templates/footer.php'; 37 | } 38 | 39 | /** 40 | * ACTION: addSong 41 | * This method handles what happens when you move to http://yourproject/songs/addsong 42 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "add a song" form on songs/index 43 | * directs the user after the form submit. This method handles all the POST data from the form and then redirects 44 | * the user back to songs/index via the last line: header(...) 45 | * This is an example of how to handle a POST request. 46 | */ 47 | public function addSong() 48 | { 49 | // simple message to show where you are 50 | echo 'Message from Controller: You are in the Controller: Songs, using the method addSong().'; 51 | 52 | // if we have POST data to create a new song entry 53 | if (isset($_POST["submit_add_song"])) { 54 | // load model, perform an action on the model 55 | $songs_model = $this->loadModel('SongsModel'); 56 | $songs_model->addSong($_POST["artist"], $_POST["track"], $_POST["link"]); 57 | } 58 | 59 | // where to go after song has been added 60 | header('location: ' . URL . 'songs/index'); 61 | } 62 | 63 | /** 64 | * ACTION: deleteSong 65 | * This method handles what happens when you move to http://yourproject/songs/deletesong 66 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "delete a song" button on songs/index 67 | * directs the user after the click. This method handles all the data from the GET request (in the URL!) and then 68 | * redirects the user back to songs/index via the last line: header(...) 69 | * This is an example of how to handle a GET request. 70 | * @param int $song_id Id of the to-delete song 71 | */ 72 | public function deleteSong($song_id) 73 | { 74 | // simple message to show where you are 75 | echo 'Message from Controller: You are in the Controller: Songs, using the method deleteSong().'; 76 | 77 | // if we have an id of a song that should be deleted 78 | if (isset($song_id)) { 79 | // load model, perform an action on the model 80 | $songs_model = $this->loadModel('SongsModel'); 81 | $songs_model->deleteSong($song_id); 82 | } 83 | 84 | // where to go after song has been deleted 85 | header('location: ' . URL . 'songs/index'); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /application/libs/application.php: -------------------------------------------------------------------------------- 1 | splitUrl(); 28 | 29 | // check for controller: does such a controller exist ? 30 | if (file_exists('./application/controller/' . $this->url_controller . '.php')) { 31 | 32 | // if so, then load this file and create this controller 33 | // example: if controller would be "car", then this line would translate into: $this->car = new car(); 34 | require './application/controller/' . $this->url_controller . '.php'; 35 | $this->url_controller = new $this->url_controller(); 36 | 37 | // check for method: does such a method exist in the controller ? 38 | if (method_exists($this->url_controller, $this->url_action)) { 39 | 40 | // call the method and pass the arguments to it 41 | if (isset($this->url_parameter_3)) { 42 | // will translate to something like $this->home->method($param_1, $param_2, $param_3); 43 | $this->url_controller->{$this->url_action}($this->url_parameter_1, $this->url_parameter_2, $this->url_parameter_3); 44 | } elseif (isset($this->url_parameter_2)) { 45 | // will translate to something like $this->home->method($param_1, $param_2); 46 | $this->url_controller->{$this->url_action}($this->url_parameter_1, $this->url_parameter_2); 47 | } elseif (isset($this->url_parameter_1)) { 48 | // will translate to something like $this->home->method($param_1); 49 | $this->url_controller->{$this->url_action}($this->url_parameter_1); 50 | } else { 51 | // if no parameters given, just call the method without parameters, like $this->home->method(); 52 | $this->url_controller->{$this->url_action}(); 53 | } 54 | } else { 55 | // default/fallback: call the index() method of a selected controller 56 | $this->url_controller->index(); 57 | } 58 | } else { 59 | // invalid URL, so simply show home/index 60 | require './application/controller/home.php'; 61 | $home = new Home(); 62 | $home->index(); 63 | } 64 | } 65 | 66 | /** 67 | * Get and split the URL 68 | */ 69 | private function splitUrl() 70 | { 71 | if (isset($_GET['url'])) { 72 | 73 | // split URL 74 | $url = rtrim($_GET['url'], '/'); 75 | $url = filter_var($url, FILTER_SANITIZE_URL); 76 | $url = explode('/', $url); 77 | 78 | // Put URL parts into according properties 79 | // By the way, the syntax here is just a short form of if/else, called "Ternary Operators" 80 | // @see http://davidwalsh.name/php-shorthand-if-else-ternary-operators 81 | $this->url_controller = (isset($url[0]) ? $url[0] : null); 82 | $this->url_action = (isset($url[1]) ? $url[1] : null); 83 | $this->url_parameter_1 = (isset($url[2]) ? $url[2] : null); 84 | $this->url_parameter_2 = (isset($url[3]) ? $url[3] : null); 85 | $this->url_parameter_3 = (isset($url[4]) ? $url[4] : null); 86 | 87 | // for debugging. uncomment this if you have problems with the URL 88 | // echo 'Controller: ' . $this->url_controller . '
'; 89 | // echo 'Action: ' . $this->url_action . '
'; 90 | // echo 'Parameter 1: ' . $this->url_parameter_1 . '
'; 91 | // echo 'Parameter 2: ' . $this->url_parameter_2 . '
'; 92 | // echo 'Parameter 3: ' . $this->url_parameter_3 . '
'; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-MVC 2 | 3 | An extremely simple and easy to understand MVC skeleton application, reduced to the max. 4 | Everything is **as simple as possible**, as **manually as possible** and as readable as possible. 5 | This project is - by intention - NOT a full framework, it's a bare-bone structure, written in 6 | purely native PHP ! The php-mvc skeleton tries to be the extremely slimmed down opposite of big frameworks 7 | like Zend2, Symfony or Laravel. 8 | 9 | ## Basic and Advanced version 10 | 11 | This is the basic version. There's another "Advanced" version in development which has 12 | additional features, currently Twig and SASS. More to come! 13 | Have a look here: https://github.com/panique/php-mvc-advanced 14 | 15 | ## Why does this project exist ? 16 | 17 | One of the biggest question in the PHP world is "How do I build an application ?". 18 | It's hard to find a good base, a good file structure and useful information on that, but at the same time 19 | there are masses of frameworks that might be really good, but really hard to understand, hard to use and extremely 20 | complex. This project tries to be some kind of naked skeleton bare-bone for quick application building, 21 | especially for the not-so-advanced coder. 22 | 23 | ### Goals of this project: 24 | 25 | - give people a clean base MVC structure to build a modern PHP application with 26 | - teach people the basics of the Model-View-Controller architecture 27 | - encourage people to code according to PSR 1/2 coding guidelines 28 | - promote the usage of PDO 29 | - promote the usage of external libraries via Composer 30 | - promote development with max. error reporting 31 | - promote to comment code 32 | - promote the usage of OOP code 33 | - using only native PHP code, so people don't have to learn a framework 34 | 35 | ## Installation 36 | 37 | ### On Windows 7 (with EasyPHP) 38 | 39 | There's a tutorial on [How to install php-mvc on Windows 7, 8 and 8.1](http://www.dev-metal.com/install-php-mvc-windows-7/). 40 | 41 | ### On Ubuntu etc. 42 | 43 | First, copy this repo into a public accessible folder on your server. 44 | Common techniques are a) downloading and extracting the .zip / .tgz by hand, b) cloning the repo with git (into var/www) 45 | 46 | ``` 47 | git clone https://github.com/panique/php-mvc.git /var/www 48 | ``` 49 | 50 | or c) getting the repo via Composer (here we copy into var/www) 51 | 52 | ``` 53 | composer create-project panique/php-mvc /var/www dev-master 54 | ``` 55 | 56 | 1. Install mod_rewrite, for example by following this guideline: 57 | [How to install mod_rewrite in Ubuntu](http://www.dev-metal.com/enable-mod_rewrite-ubuntu-12-04-lts/) 58 | 59 | 2. Run the SQL statements in the *application/_install* folder. 60 | 61 | 3. Change the .htaccess file from 62 | ``` 63 | RewriteBase /php-mvc/ 64 | ``` 65 | to where you put this project, relative to the web root folder (usually /var/www). So when you put this project into 66 | the web root, like directly in /var/www, then the line should look like or can be commented out: 67 | ``` 68 | RewriteBase / 69 | ``` 70 | If you have put the project into a sub-folder, then put the name of the sub-folder here: 71 | ``` 72 | RewriteBase /sub-folder/ 73 | ``` 74 | 75 | 4. Edit the *application/config/config.php*, change this line 76 | ```php 77 | define('URL', 'http://127.0.0.1/php-mvc/'); 78 | ``` 79 | to where your project is. Real domain, IP or 127.0.0.1 when developing locally. Make sure you put the sub-folder 80 | in here (when installing in a sub-folder) too, also don't forget the trailing slash ! 81 | 82 | 5. Edit the *application/config/config.php*, change these lines 83 | ```php 84 | define('DB_TYPE', 'mysql'); 85 | define('DB_HOST', '127.0.0.1'); 86 | define('DB_NAME', 'php-mvc'); 87 | define('DB_USER', 'root'); 88 | define('DB_PASS', 'mysql'); 89 | ``` 90 | to your database credentials. If you don't have an empty database, create one. Only change the type `mysql` if you 91 | know what you are doing. 92 | 93 | ## A quickstart tutorial 94 | 95 | You can also find these tutorial pictures in the *_tutorial* folder. 96 | 97 | ![php-mvc introduction tutorial - page 1](_tutorial/tutorial-part-01.png) 98 | ![php-mvc introduction tutorial - page 2](_tutorial/tutorial-part-02.png) 99 | ![php-mvc introduction tutorial - page 3](_tutorial/tutorial-part-03.png) 100 | ![php-mvc introduction tutorial - page 4](_tutorial/tutorial-part-04.png) 101 | ![php-mvc introduction tutorial - page 5](_tutorial/tutorial-part-05.png) 102 | 103 | ## You like what you see ? 104 | 105 | Then please also have a look on ... 106 | 107 | #### My other project php-login 108 | 109 | A collection of 4 similar login scripts for PHP, from a super-simple one-file 110 | script with a SQLite one-file to a highly professional MVC frameworks solution. All scripts use the most advanced 111 | hashing algorithms possible in PHP, exactly like the PHP core developers want you to use them. 112 | 113 | https://github.com/panique/php-login (full MVC framework) 114 | 115 | https://github.com/panique/php-login-minimal (minimal) 116 | 117 | https://github.com/panique/php-login-advanced (advanced) 118 | 119 | https://github.com/panique/php-login-one-file (one-file) 120 | 121 | #### My PHP and frontend blog 122 | 123 | Lots of non-boring development stuff and tutorials there. 124 | 125 | http://www.dev-metal.com 126 | 127 | ## Useful information 128 | 129 | 1. SQLite does not have a rowCount() method (!). Keep that in mind in case you use SQLite. 130 | 131 | 2. Don't use the same name for class and method, as this might trigger an (unintended) *__construct* of the class. 132 | This is really weird behaviour, but documented here: [php.net - Constructors and Destructors](http://php.net/manual/en/language.oop5.decon.php). 133 | 134 | ## Add external libraries via Composer 135 | 136 | To add external libraries/tools/whatever into your project in an extremely clean way, simply add a line with the 137 | repo name and version to the composer.json! Take a look on these tutorials if you want to get into Composer: 138 | [How to install (and update) Composer on Windows 7 or Ubuntu / Debian](http://www.dev-metal.com/install-update-composer-windows-7-ubuntu-debian-centos/) 139 | and [Getting started with Composer](http://www.dev-metal.com/getting-started-composer/). 140 | 141 | ## License 142 | 143 | This project is licensed under the MIT License. 144 | This means you can use and modify it for free in private or commercial projects. 145 | 146 | ## Contribute 147 | 148 | Please commit into the develop branch (which holds the in-development version), not into master branch 149 | (which holds the tested and stable version). 150 | 151 | ## Support / Donate 152 | 153 | If you think this script is useful and saves you a lot of work, then think about supporting the project: 154 | 155 | 1. Donate via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=P5YLUK4MW3LDG) or [GitTip](https://www.gittip.com/Panique/) 156 | 2. Rent your next server at [A2 Hosting](http://www.a2hosting.com/4471.html) or [DigitalOcean](https://www.digitalocean.com/?refcode=40d978532a20). 157 | 3. Contribute to this project. Feel free to improve this project with your skills. 158 | 4. Spread the word: Tell others about this project. 159 | 160 | ## Linked music tracks in the demo application 161 | 162 | The linked tracks in this naked application are just some of my personal favourites of the last few months. 163 | I think it's always a good idea to fill boring nerd-code stuff with quality culture. 164 | 165 | ## Statistics (by BitDeli) 166 | 167 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/panique/php-mvc/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 168 | --------------------------------------------------------------------------------