Browse Source

adding PIMF php micro framework FULL

gjerokrsteski 11 years ago
parent
commit
d66f1e575e
100 changed files with 10615 additions and 1 deletions
  1. 18 0
      php-pimf/pimf-framework/.editorconfig
  2. 1 0
      php-pimf/pimf-framework/.gitattributes
  3. 2 1
      php-pimf/pimf-framework/.gitignore
  4. 68 0
      php-pimf/pimf-framework/.scrutinizer.yml
  5. 20 0
      php-pimf/pimf-framework/.travis.yml
  6. 52 0
      php-pimf/pimf-framework/README.md
  7. 114 0
      php-pimf/pimf-framework/autoload.core.php
  8. 11 0
      php-pimf/pimf-framework/bootstrap.core.php
  9. 25 0
      php-pimf/pimf-framework/composer.json
  10. 222 0
      php-pimf/pimf-framework/core/Pimf/Application.php
  11. 115 0
      php-pimf/pimf-framework/core/Pimf/Cache.php
  12. 110 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Apc.php
  13. 240 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Dba.php
  14. 107 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/File.php
  15. 96 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Memcached.php
  16. 85 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Memory.php
  17. 136 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Pdo.php
  18. 98 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Redis.php
  19. 137 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Storage.php
  20. 89 0
      php-pimf/pimf-framework/core/Pimf/Cache/Storages/Wincache.php
  21. 143 0
      php-pimf/pimf-framework/core/Pimf/Cli.php
  22. 81 0
      php-pimf/pimf-framework/core/Pimf/Cli/Color.php
  23. 105 0
      php-pimf/pimf-framework/core/Pimf/Cli/Std.php
  24. 26 0
      php-pimf/pimf-framework/core/Pimf/Contracts/Arrayable.php
  25. 28 0
      php-pimf/pimf-framework/core/Pimf/Contracts/Cleanable.php
  26. 28 0
      php-pimf/pimf-framework/core/Pimf/Contracts/Jsonable.php
  27. 26 0
      php-pimf/pimf-framework/core/Pimf/Contracts/MessageProvider.php
  28. 26 0
      php-pimf/pimf-framework/core/Pimf/Contracts/Renderable.php
  29. 26 0
      php-pimf/pimf-framework/core/Pimf/Contracts/Reunitable.php
  30. 115 0
      php-pimf/pimf-framework/core/Pimf/Controller/Base.php
  31. 135 0
      php-pimf/pimf-framework/core/Pimf/Controller/Core.php
  32. 22 0
      php-pimf/pimf-framework/core/Pimf/Controller/Exception.php
  33. 199 0
      php-pimf/pimf-framework/core/Pimf/Cookie.php
  34. 71 0
      php-pimf/pimf-framework/core/Pimf/DataMapper/Base.php
  35. 82 0
      php-pimf/pimf-framework/core/Pimf/Database.php
  36. 103 0
      php-pimf/pimf-framework/core/Pimf/EntityManager.php
  37. 231 0
      php-pimf/pimf-framework/core/Pimf/Environment.php
  38. 134 0
      php-pimf/pimf-framework/core/Pimf/Error.php
  39. 177 0
      php-pimf/pimf-framework/core/Pimf/Event.php
  40. 234 0
      php-pimf/pimf-framework/core/Pimf/Logger.php
  41. 110 0
      php-pimf/pimf-framework/core/Pimf/Memcached.php
  42. 55 0
      php-pimf/pimf-framework/core/Pimf/Model/AsArray.php
  43. 79 0
      php-pimf/pimf-framework/core/Pimf/Param.php
  44. 55 0
      php-pimf/pimf-framework/core/Pimf/Pdo/Connector.php
  45. 49 0
      php-pimf/pimf-framework/core/Pimf/Pdo/Factory.php
  46. 45 0
      php-pimf/pimf-framework/core/Pimf/Pdo/Mysql.php
  47. 53 0
      php-pimf/pimf-framework/core/Pimf/Pdo/Postgre.php
  48. 36 0
      php-pimf/pimf-framework/core/Pimf/Pdo/Sqlite.php
  49. 46 0
      php-pimf/pimf-framework/core/Pimf/Pdo/Sqlserver.php
  50. 322 0
      php-pimf/pimf-framework/core/Pimf/Redis.php
  51. 107 0
      php-pimf/pimf-framework/core/Pimf/Registry.php
  52. 133 0
      php-pimf/pimf-framework/core/Pimf/Request.php
  53. 106 0
      php-pimf/pimf-framework/core/Pimf/Resolver.php
  54. 20 0
      php-pimf/pimf-framework/core/Pimf/Resolver/Exception.php
  55. 254 0
      php-pimf/pimf-framework/core/Pimf/Response.php
  56. 174 0
      php-pimf/pimf-framework/core/Pimf/Route.php
  57. 92 0
      php-pimf/pimf-framework/core/Pimf/Route/Target.php
  58. 85 0
      php-pimf/pimf-framework/core/Pimf/Router.php
  59. 77 0
      php-pimf/pimf-framework/core/Pimf/Sapi.php
  60. 164 0
      php-pimf/pimf-framework/core/Pimf/Session.php
  61. 337 0
      php-pimf/pimf-framework/core/Pimf/Session/Payload.php
  62. 61 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Apc.php
  63. 59 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Cookie.php
  64. 79 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Dba.php
  65. 91 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/File.php
  66. 63 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Memcached.php
  67. 57 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Memory.php
  68. 116 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Pdo.php
  69. 63 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Redis.php
  70. 78 0
      php-pimf/pimf-framework/core/Pimf/Session/Storages/Storage.php
  71. 77 0
      php-pimf/pimf-framework/core/Pimf/Uri.php
  72. 232 0
      php-pimf/pimf-framework/core/Pimf/Url.php
  73. 75 0
      php-pimf/pimf-framework/core/Pimf/Util/Dom.php
  74. 105 0
      php-pimf/pimf-framework/core/Pimf/Util/File.php
  75. 204 0
      php-pimf/pimf-framework/core/Pimf/Util/Header.php
  76. 70 0
      php-pimf/pimf-framework/core/Pimf/Util/Header/ContentType.php
  77. 149 0
      php-pimf/pimf-framework/core/Pimf/Util/Header/ResponseStatus.php
  78. 108 0
      php-pimf/pimf-framework/core/Pimf/Util/Identifier.php
  79. 96 0
      php-pimf/pimf-framework/core/Pimf/Util/IdentityMap.php
  80. 81 0
      php-pimf/pimf-framework/core/Pimf/Util/Json.php
  81. 249 0
      php-pimf/pimf-framework/core/Pimf/Util/Ldap.php
  82. 132 0
      php-pimf/pimf-framework/core/Pimf/Util/Ldap/User.php
  83. 81 0
      php-pimf/pimf-framework/core/Pimf/Util/LineByLine.php
  84. 140 0
      php-pimf/pimf-framework/core/Pimf/Util/Message.php
  85. 142 0
      php-pimf/pimf-framework/core/Pimf/Util/Serializer.php
  86. 281 0
      php-pimf/pimf-framework/core/Pimf/Util/String.php
  87. 119 0
      php-pimf/pimf-framework/core/Pimf/Util/String/Clean.php
  88. 220 0
      php-pimf/pimf-framework/core/Pimf/Util/Uploaded.php
  89. 118 0
      php-pimf/pimf-framework/core/Pimf/Util/Uploaded/Factory.php
  90. 140 0
      php-pimf/pimf-framework/core/Pimf/Util/Uuid.php
  91. 303 0
      php-pimf/pimf-framework/core/Pimf/Util/Validator.php
  92. 68 0
      php-pimf/pimf-framework/core/Pimf/Util/Validator/Factory.php
  93. 132 0
      php-pimf/pimf-framework/core/Pimf/Util/Value.php
  94. 115 0
      php-pimf/pimf-framework/core/Pimf/Util/Xml.php
  95. 190 0
      php-pimf/pimf-framework/core/Pimf/View.php
  96. 75 0
      php-pimf/pimf-framework/core/Pimf/View/Haanga.php
  97. 92 0
      php-pimf/pimf-framework/core/Pimf/View/Twig.php
  98. 6 0
      php-pimf/pimf-framework/core/Pimf/_database/create-cache-table-mysql.sql
  99. 5 0
      php-pimf/pimf-framework/core/Pimf/_database/create-cache-table-sqlite.sql
  100. 6 0
      php-pimf/pimf-framework/core/Pimf/_database/create-session-table-mysql.sql

+ 18 - 0
php-pimf/pimf-framework/.editorconfig

@@ -0,0 +1,18 @@
+; top-most EditorConfig file
+root = true
+
+; Unix-style newlines
+[*]
+end_of_line = LF
+
+[*.php]
+indent_style = space
+indent_size = 2
+
+[*.test]
+indent_style = space
+indent_size = 2
+
+[*.rst]
+indent_style = space
+indent_size = 2

+ 1 - 0
php-pimf/pimf-framework/.gitattributes

@@ -0,0 +1 @@
+/tests export-ignore

+ 2 - 1
php-pimf/.gitignore → php-pimf/pimf-framework/.gitignore

@@ -27,4 +27,5 @@ nbproject
 intermediate
 publish
 .idea
-pimf-framework
+_coverage
+coverage

+ 68 - 0
php-pimf/pimf-framework/.scrutinizer.yml

@@ -0,0 +1,68 @@
+imports:
+    - php
+
+tools:
+
+    # Code Coverage
+    external_code_coverage:
+        enabled: true
+        filter:
+            excluded_paths:
+                - 'tests/*'
+
+    # Copy/Paste Detector
+    php_cpd:
+        enabled:              true
+        command:              phpcpd
+        excluded_dirs:
+            - tests
+        
+    
+    # PHP CS Fixer (http://http://cs.sensiolabs.org/).
+    php_cs_fixer:
+        enabled:              true
+        command:              php-cs-fixer
+        config:
+            level:            psr2
+        filter:
+            excluded_paths:
+                - 'tests/*'
+        
+    
+    # Analyzes the size and structure of a PHP project.
+    php_loc:
+        enabled:              true
+        command:              phploc
+        excluded_dirs:
+            - tests
+        
+    
+    # PHP Mess Detector (http://phpmd.org).
+    php_mess_detector:
+        enabled:              true
+        command:              phpmd
+        config:
+            rulesets:
+                - codesize
+                - unusedcode
+                - naming
+                - design
+        filter:
+            excluded_paths:
+                - 'tests/*'
+        
+    
+    # Analyzes the size and structure of a PHP project.
+    php_pdepend:
+        enabled:              true
+        command:              pdepend
+        excluded_dirs:
+            - tests
+        
+    # Runs Scrutinizer's PHP Analyzer Tool
+    php_analyzer:
+        enabled:              true
+        filter:
+            excluded_paths:
+                - 'tests/*'
+

+ 20 - 0
php-pimf/pimf-framework/.travis.yml

@@ -0,0 +1,20 @@
+language: php
+
+php:
+  - 5.3
+  - 5.4
+  - 5.5
+  - 5.6
+
+before_script:
+  - phpenv rehash
+
+script:
+  - phpunit --configuration phpunit.xml --coverage-clover=coverage.clover
+  - wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
+
+notifications:
+  email:
+    - [email protected]
+

+ 52 - 0
php-pimf/pimf-framework/README.md

@@ -0,0 +1,52 @@
+Welcome to PIMF
+===============
+Have you ever wished a PHP framework that perfectly adapts to your projects needs, your programming experience and your customers budget? A thin PHP framewrok with less implementing rools and easy to learn how to use it? PIMF is about to satisfy your demands!
+
+[![Build Status](https://travis-ci.org/gjerokrsteski/pimf-framework.png?branch=master)](https://travis-ci.org/gjerokrsteski/pimf-framework) 
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/gjerokrsteski/pimf-framework/badges/quality-score.png?s=6455d019460628327434d85520bd13c4d03a2673)](https://scrutinizer-ci.com/g/gjerokrsteski/pimf-framework/)
+[![Code Coverage](https://scrutinizer-ci.com/g/gjerokrsteski/pimf-framework/badges/coverage.png?s=ded7f154ac78cbdbabc166e32fd2f54c009d2c67)](https://scrutinizer-ci.com/g/gjerokrsteski/pimf-framework/)
+
+**Note:** This repository contains the core code of the PIMF framework. If you want to build an application using PIMF, please use ono of the quick starting bundles below.
+
+PIMF Philosophy
+---------------
+A good and robust business-logic is better that fat and complex framework. Most of the PHP framewroks are bigger than your problem. At all you need less than 20% of the functionality of the framework to solve you problem. Therefore we belive that the “right” 20% of the effort is the 80% of the results - and that is PIMF.
+
+The aim was to create robust and secure projects and deliver them fast. We wanted just one easy framewrok, who can be used once for all  our projects. And than - PIMF was born!
+
+PIMFs implementation is based on well proved design patterns as well as fast objects relation mapping mechanism - like famous PHP frameworks had. The architecture is designed upgrade friendly - so you can upgrade to newer versions without to override your projects. And for all of you out there, who like to create rich application interfaces with ExtJs or Dojo - we have created mechanism to couple your GUI to the controllers in a easy and fast way.
+
+
+Quickstart with PIMF-Blog bundle using SQLite
+---------------------------------------------
+This Blog is a run ready bundle, which uses PIMF framework including a blog application based on SQLite database. Here you can learn how to work with \Pimf\EntityManager, \Pimf\Util\Validator and \Pimf\View. https://github.com/gjerokrsteski/pimf-blog
+
+Quickstart with PIMF-Blog bundle using MySQL and SQLite
+-------------------------------------------------------
+This Blog is a run ready bundle, which uses PIMF framework including a blog application based on MySQL database. The session will be stored at SQLite database.
+Here you can learn how to work with Pimf\EntityManager, Pimf\Util\Validator and Pimf\View. https://github.com/gjerokrsteski/pimf-blog-mysql
+
+Quickstart with PIMF-Vanilla bundle
+------------------------------
+This is a run ready "Hello world" bundle using PIMF micro framework. Here you can learn how to work with \Pimf\View and \Pimf\Router. https://github.com/gjerokrsteski/pimf-vanilla
+
+Quickstart with PIMF-Haanga bundle
+-----------------------------
+This is a run ready "Hello world" application using PIMF micro framework and Haanga (http://haanga.org/documentation) a fast and secure template engine for PHP that uses Django syntax.
+Here you can learn how to work with \Pimf\View\Haanga template engine and \Pimf\Router. https://github.com/gjerokrsteski/pimf-haanga
+
+Quickstart with PIMF-Twig bundle
+-----------------------------
+This is a run ready "Hello world" application using PIMF micro framework and Twig a flexible, fast, and secure template
+engine for PHP (http://twig.sensiolabs.org) brought for us by Symfony framework developers. Here you can learn how to work
+with \Pimf\View\Twig template engine and \Pimf\Router. https://github.com/gjerokrsteski/pimf-twig
+
+Learning PIMF
+-------------
+One of the best ways to learn PIMF is to read through the entirety of its documentation. This guide details all aspects of the framework and how to apply them to your application.
+
+Please read here: https://github.com/gjerokrsteski/pimf/wiki
+
+Framework Sponsor
+-------------------
+JetBRAINS supports the development of the PIMF with PHPStorm licenses and we feel confidential that PHPStorm strongly influences the PIMF's quality. Use PHPStorm! http://www.jetbrains.com/phpstorm/

+ 114 - 0
php-pimf/pimf-framework/autoload.core.php

@@ -0,0 +1,114 @@
+<?php
+// @codingStandardsIgnoreFile
+// @codeCoverageIgnoreStart
+// this is an auto-generated file, please do not edit!
+spl_autoload_register(
+  function ($class) {
+    static $classes = null;
+    if ($classes === null) {
+      $classes = array(
+        'Pimf\\Application'                  => '/Pimf/Application.php',
+        'Pimf\\Cache'                        => '/Pimf/Cache.php',
+        'Pimf\\Cache\\Storages\\Apc'         => '/Pimf/Cache/Storages/Apc.php',
+        'Pimf\\Cache\\Storages\\Dba'         => '/Pimf/Cache/Storages/Dba.php',
+        'Pimf\\Cache\\Storages\\File'        => '/Pimf/Cache/Storages/File.php',
+        'Pimf\\Cache\\Storages\\Memcached'   => '/Pimf/Cache/Storages/Memcached.php',
+        'Pimf\\Cache\\Storages\\Memory'      => '/Pimf/Cache/Storages/Memory.php',
+        'Pimf\\Cache\\Storages\\Database'    => '/Pimf/Cache/Storages/Database.php',
+        'Pimf\\Cache\\Storages\\Redis'       => '/Pimf/Cache/Storages/Redis.php',
+        'Pimf\\Cache\\Storages\\Storage'     => '/Pimf/Cache/Storages/Storage.php',
+        'Pimf\\Cache\\Storages\\Wincache'    => '/Pimf/Cache/Storages/Wincache.php',
+        'Pimf\\Cache\\Storages\\Pdo'         => '/Pimf/Cache/Storages/Pdo.php',
+        'Pimf\\Cli'                          => '/Pimf/Cli.php',
+        'Pimf\\Cli\\Color'                   => '/Pimf/Cli/Color.php',
+        'Pimf\\Cli\\Std'                      => '/Pimf/Cli/Std.php',
+        'Pimf\\Contracts\\Arrayable'         => '/Pimf/Contracts/Arrayable.php',
+        'Pimf\\Contracts\\Cleanable'         => '/Pimf/Contracts/Cleanable.php',
+        'Pimf\\Contracts\\Jsonable'          => '/Pimf/Contracts/Jsonable.php',
+        'Pimf\\Contracts\\MessageProvider'   => '/Pimf/Contracts/MessageProvider.php',
+        'Pimf\\Contracts\\Renderable'        => '/Pimf/Contracts/Renderable.php',
+        'Pimf\\Contracts\\Reunitable'        => '/Pimf/Contracts/Reunitable.php',
+        'Pimf\\Controller\\Base'             => '/Pimf/Controller/Base.php',
+        'Pimf\\Controller\\Core'             => '/Pimf/Controller/Core.php',
+        'Pimf\\Controller\\Exception'        => '/Pimf/Controller/Exception.php',
+        'Pimf\\Cookie'                       => '/Pimf/Cookie.php',
+        'Pimf\\DataMapper\\Base'             => '/Pimf/DataMapper/Base.php',
+        'Pimf\\EntityManager'                => '/Pimf/EntityManager.php',
+        'Pimf\\Environment'                  => '/Pimf/Environment.php',
+        'Pimf\\Error'                        => '/Pimf/Error.php',
+        'Pimf\\Event'                        => '/Pimf/Event.php',
+        'Pimf\\Logger'                       => '/Pimf/Logger.php',
+        'Pimf\\Memcached'                    => '/Pimf/Memcached.php',
+        'Pimf\\Model\\AsArray'               => '/Pimf/Model/AsArray.php',
+        'Pimf\\Param'                        => '/Pimf/Param.php',
+        'Pimf\\Database'                     => '/Pimf/Database.php',
+        'Pimf\\Pdo\\Connector'               => '/Pimf/Pdo/Connector.php',
+        'Pimf\\Pdo\\Factory'                 => '/Pimf/Pdo/Factory.php',
+        'Pimf\\Pdo\\Mysql'                   => '/Pimf/Pdo/Mysql.php',
+        'Pimf\\Pdo\\Postgre'                 => '/Pimf/Pdo/Postgre.php',
+        'Pimf\\Pdo\\Sqlite'                  => '/Pimf/Pdo/Sqlite.php',
+        'Pimf\\Pdo\\Sqlserver'               => '/Pimf/Pdo/Sqlserver.php',
+        'Pimf\\Redis'                        => '/Pimf/Redis.php',
+        'Pimf\\Registry'                     => '/Pimf/Registry.php',
+        'Pimf\\Request'                      => '/Pimf/Request.php',
+        'Pimf\\Response'                     => '/Pimf/Response.php',
+        'Pimf\\Resolver'                     => '/Pimf/Resolver.php',
+        'Pimf\\Route'                        => '/Pimf/Route.php',
+        'Pimf\\Route\\Target'                => '/Pimf/Route/Target.php',
+        'Pimf\\Router'                       => '/Pimf/Router.php',
+        'Pimf\\Resolver\\Exception'          => '/Pimf/Resolver/Exception.php',
+        'Pimf\\Sapi'                         => '/Pimf/Sapi.php',
+        'Pimf\\Session'                      => '/Pimf/Session.php',
+        'Pimf\\Session\\Payload'             => '/Pimf/Session/Payload.php',
+        'Pimf\\Session\\Storages\\Apc'       => '/Pimf/Session/Storages/Apc.php',
+        'Pimf\\Session\\Storages\\Cookie'    => '/Pimf/Session/Storages/Cookie.php',
+        'Pimf\\Session\\Storages\\Dba'       => '/Pimf/Session/Storages/Dba.php',
+        'Pimf\\Session\\Storages\\File'      => '/Pimf/Session/Storages/File.php',
+        'Pimf\\Session\\Storages\\Memcached' => '/Pimf/Session/Storages/Memcached.php',
+        'Pimf\\Session\\Storages\\Memory'    => '/Pimf/Session/Storages/Memory.php',
+        'Pimf\\Session\\Storages\\Database'  => '/Pimf/Session/Storages/Database.php',
+        'Pimf\\Session\\Storages\\Redis'     => '/Pimf/Session/Storages/Redis.php',
+        'Pimf\\Session\\Storages\\Storage'   => '/Pimf/Session/Storages/Storage.php',
+        'Pimf\\Session\\Storages\\Pdo'       => '/Pimf/Session/Storages/Pdo.php',
+        'Pimf\\Uri'                          => '/Pimf/Uri.php',
+        'Pimf\\Url'                          => '/Pimf/Url.php',
+        'Pimf\\Util\\Cache'                  => '/Pimf/Util/Cache.php',
+        'Pimf\\Util\\Crypter'                => '/Pimf/Util/Crypter.php',
+        'Pimf\\Util\\Csv'                    => '/Pimf/Util/Csv.php',
+        'Pimf\\Util\\Dom'                    => '/Pimf/Util/Dom.php',
+        'Pimf\\Util\\File'                   => '/Pimf/Util/File.php',
+        'Pimf\\Util\\Header'                 => '/Pimf/Util/Header.php',
+        'Pimf\\Util\\Header\\ResponseStatus' => '/Pimf/Util/Header/ResponseStatus.php',
+        'Pimf\\Util\\Header\\ContentType'    => '/Pimf/Util/Header/ContentType.php',
+        'Pimf\\Util\\Identifier'             => '/Pimf/Util/Identifier.php',
+        'Pimf\\Util\\IdentityMap'            => '/Pimf/Util/IdentityMap.php',
+        'Pimf\\Util\\Json'                   => '/Pimf/Util/Json.php',
+        'Pimf\\Util\\Ldap'                   => '/Pimf/Util/Ldap.php',
+        'Pimf\\Util\\Ldap\\User'             => '/Pimf/Util/Ldap/User.php',
+        'Pimf\\Util\\LineByLine'             => '/Pimf/Util/LineByLine.php',
+        'Pimf\\Util\\Message'                => '/Pimf/Util/Message.php',
+        'Pimf\\Util\\Serializer'             => '/Pimf/Util/Serializer.php',
+        'Pimf\\Util\\String'                 => '/Pimf/Util/String.php',
+        'Pimf\\Util\\String\\Clean'          => '/Pimf/Util/String/Clean.php',
+        'Pimf\\Util\\Uploaded'               => '/Pimf/Util/Uploaded.php',
+        'Pimf\\Util\\Uploaded\\Factory'      => '/Pimf/Util/Uploaded/Factory.php',
+        'Pimf\\Util\\Uuid'                   => '/Pimf/Util/Uuid.php',
+        'Pimf\\Util\\Validator'              => '/Pimf/Util/Validator.php',
+        'Pimf\\Util\\Validator\\Factory'     => '/Pimf/Util/Validator/Factory.php',
+        'Pimf\\Util\\Value'                  => '/Pimf/Util/Value.php',
+        'Pimf\\Util\\Xml'                    => '/Pimf/Util/Xml.php',
+        'Pimf\\View'                         => '/Pimf/View.php',
+        'Pimf\\View\\Haanga'                 => '/Pimf/View/Haanga.php',
+        'Pimf\\View\\Json'                   => '/Pimf/View/Json.php',
+        'Pimf\\View\\Twig'                   => '/Pimf/View/Twig.php'
+      );
+    }
+
+    if (isset($classes[$class])) {
+      require __DIR__ . '/core' . $classes[$class];
+    }
+
+    return false;
+  }
+);
+// @codeCoverageIgnoreEnd

+ 11 - 0
php-pimf/pimf-framework/bootstrap.core.php

@@ -0,0 +1,11 @@
+<?php
+/*
+|--------------------------------------------------------------------------
+| PIMF bootstrap
+|--------------------------------------------------------------------------
+*/
+if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR, true);
+if(!defined('BASE_PATH')) define('BASE_PATH', realpath(__DIR__) . DS, true);
+
+require_once 'autoload.core.php';
+require_once 'utils.php';

+ 25 - 0
php-pimf/pimf-framework/composer.json

@@ -0,0 +1,25 @@
+{
+    "name": "gjerokrsteski/pimf-framework",
+    "description": "Micro framework for PHP that emphasises minimalism and simplicity",
+    "version": "1.8.*",
+    "license": "BSD",
+    "homepage": "http://pimf-framework.de",
+    "minimum-stability": "stable",
+    "keywords": ["framework", "pimf"],
+    "support": {
+        "email": "[email protected]",
+        "wiki": "https://github.com/gjerokrsteski/pimf/wiki",
+        "source": "https://github.com/gjerokrsteski/pimf-framework"
+    },
+    "authors" : [
+    {
+      "name" : "Gjero Krsteski",
+      "email" : "[email protected]",
+      "homepage" : "http://krsteski.de/",
+      "role" : "Developer"
+    }
+  ],
+  "require" : {
+    "php" : ">5.2"
+  }
+}

+ 222 - 0
php-pimf/pimf-framework/core/Pimf/Application.php

@@ -0,0 +1,222 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c) Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Util\String as Str;
+
+/**
+ * Provides a facility for applications which provides reusable resources,
+ * common-based bootstrapping and dependency checking.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ */
+final class Application
+{
+  const VERSION = '1.8.6';
+
+  /**
+   * Mechanism used to do some initial config before a Application runs.
+   *
+   * @param array $conf   The array of configuration options.
+   * @param array $server Array of information such as headers, paths, and script locations.
+   *
+   * @return boolean|null
+   */
+  public static function bootstrap(array $conf, array $server = array())
+  {
+    $problems = array();
+
+    try {
+
+      ini_set('default_charset', $conf['encoding']);
+      date_default_timezone_set($conf['timezone']);
+
+      self::registerLocalEnvironment($conf, $server);
+      self::setupErrorHandling($conf);
+      self::loadPdoDriver($conf);
+      self::loadRoutes($conf['app']['routeable'], BASE_PATH . 'app/' . $conf['app']['name'] . '/routes.php');
+      self::loadListeners(BASE_PATH . 'app/' . $conf['app']['name'] . '/events.php');
+
+    } catch (\Exception $exception) {
+      $problems[] = $exception->getMessage();
+    }
+
+    self::reportIf($problems, PHP_VERSION);
+  }
+
+  /**
+   * Please bootstrap first, than run the application!
+   *
+   * Run a application, let application accept a request, route the request,
+   * dispatch to controller/action, render response and return response to client finally.
+   *
+   * @param array $get    Array of variables passed to the current script via the URL parameters.
+   * @param array $post   Array of variables passed to the current script via the HTTP POST method.
+   * @param array $cookie Array of variables passed to the current script via HTTP Cookies.
+   *
+   * @throws \LogicException If application not bootstrapped.
+   * @return void
+   */
+  public static function run(array $get, array $post, array $cookie)
+  {
+    $cli = array();
+    if (Sapi::isCli()) {
+      $cli = Cli::parse((array)Registry::get('env')->argv);
+      if (count($cli) < 1 || isset($cli['list'])) {
+        Cli::absorb();
+        exit(0);
+      }
+    }
+
+    $conf       = Registry::get('conf');
+    $prefix     = Str::ensureTrailing('\\', $conf['app']['name']);
+    $repository = BASE_PATH . 'app/' . $conf['app']['name'] . '/Controller';
+
+    if (isset($cli['controller']) && $cli['controller'] == 'core') {
+      $prefix     = 'Pimf\\';
+      $repository = BASE_PATH . 'pimf-framework/core/Pimf/Controller';
+    }
+
+    $resolver = new Resolver(new Request($get, $post, $cookie, $cli), $repository, $prefix);
+
+    $sessionized = (Sapi::isWeb() && $conf['session']['storage'] !== '');
+
+    if ($sessionized) {
+      Session::load();
+    }
+
+    $pimf = $resolver->process();
+
+    if ($sessionized) {
+      Session::save();
+      Cookie::send();
+    }
+
+    $pimf->render();
+  }
+
+  /**
+   * @param array $conf
+   * @param array $server
+   */
+  private static function registerLocalEnvironment(array $conf, array $server)
+  {
+    Registry::set('conf', $conf);
+    Registry::set('env', new Environment($server));
+    Registry::set('logger', new Logger($conf['bootstrap']['local_temp_directory']));
+
+    Registry::get('logger')->init();
+  }
+
+  /**
+   * @param array $conf
+   */
+  private static function setupErrorHandling(array $conf)
+  {
+    if ($conf['environment'] == 'testing') {
+      error_reporting(E_ALL | E_STRICT);
+    } else {
+
+      set_exception_handler(
+        function ($exception) {
+          Error::exception($exception);
+        }
+      );
+
+      set_error_handler(
+        function ($code, $error, $file, $line) {
+          Error::native($code, $error, $file, $line);
+        }
+      );
+
+      register_shutdown_function(
+        function () {
+          Error::shutdown();
+        }
+      );
+
+      error_reporting(-1);
+    }
+  }
+
+  /**
+   * @param array $conf
+   */
+  private static function loadPdoDriver(array $conf)
+  {
+    $dbConf = $conf[$conf['environment']]['db'];
+
+    if (is_array($dbConf) && $conf['environment'] != 'testing') {
+      Registry::set('em', new EntityManager(Pdo\Factory::get($dbConf), $conf['app']['name']));
+    }
+  }
+
+  /**
+   * @param boolean $routeable
+   * @param string  $routes Path to routes definition file.
+   */
+  private static function loadRoutes($routeable, $routes)
+  {
+    if ($routeable === true && file_exists($routes)) {
+
+      Registry::set('router', new Router());
+
+      foreach ((array)(include $routes) as $route) {
+
+        Registry::get('router')->map($route);
+
+      }
+    }
+  }
+
+  /**
+   * @param string $events Path to event listeners
+   */
+  private static function loadListeners($events)
+  {
+    if (file_exists($events)) {
+      include_once $events;
+    }
+  }
+
+  /**
+   * @param array $problems
+   * @param float $version
+   * @param bool  $die
+   *
+   * @return array|void
+   */
+  private static function reportIf(array $problems, $version, $die = true)
+  {
+    if (version_compare($version, 5.3) == -1) {
+      $problems[] = 'You have PHP ' . $version . ' and you need 5.3 or higher!';
+    }
+
+    if (!empty($problems)) {
+      return ($die === true) ? die(implode(PHP_EOL . PHP_EOL, $problems)) : $problems;
+    }
+  }
+
+  /**
+   * PIMF Application can not be cloned.
+   */
+  private function __clone() { }
+
+  /**
+   * Stopping the PHP process for PHP-FastCGI users to speed up some PHP queries.
+   */
+  public static function finish()
+  {
+    if (function_exists('fastcgi_finish_request')) {
+      fastcgi_finish_request();
+    }
+  }
+}

+ 115 - 0
php-pimf/pimf-framework/core/Pimf/Cache.php

@@ -0,0 +1,115 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Util\String;
+use Pimf\Cache\Storages as CS;
+
+/**
+ * Cache usage
+ *
+ * <code>
+ *    // Get the default cache storage instance
+ *    $storage = Cache::storage();
+ *
+ *    // Get a specific cache storage instance by name
+ *    $storage = Cache::storage('memcached');
+ *
+ *    // Call the "get" method on the default cache storage
+ *    $name = Cache::get('name');
+ *
+ *    // Call the "put" method on the default cache storage
+ *    Cache::put('name', 'Robin', 15);
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Cache
+{
+  /**
+   * All of the active cache storages.
+   *
+   * @var \Pimf\Cache\Storages\Storage[]
+   */
+  public static $storages = array();
+
+  /**
+   * Get a cache storage instance.
+   *
+   * @param string $storage
+   *
+   * @return CS\Apc|CS\Dba|CS\File|CS\Memcached|CS\Memory|CS\Pdo|CS\Redis|CS\WinCache
+   */
+  public static function storage($storage = 'memory')
+  {
+    if (!isset(static::$storages[$storage])) {
+      static::$storages[$storage] = static::factory($storage);
+    }
+
+    return static::$storages[$storage];
+  }
+
+  /**
+   * Create a new cache storage instance.
+   *
+   * @param string $storage
+   *
+   * @return CS\Apc|CS\Dba|CS\File|CS\Memcached|CS\Memory|CS\Pdo|CS\Redis|CS\WinCache
+   * @throws \RuntimeException
+   */
+  protected static function factory($storage)
+  {
+    $conf = Registry::get('conf');
+
+    switch ($storage) {
+      case 'apc':
+        return new CS\Apc($conf['cache']['key']);
+
+      case 'file':
+        return new CS\File($conf['cache']['storage_path']);
+
+      case 'pdo':
+        return new CS\Pdo(Pdo\Factory::get($conf['cache']['database']), $conf['cache']['key']);
+
+      case 'memcached':
+        return new CS\Memcached(Memcached::connection(), $conf['cache']['key']);
+
+      case 'memory':
+        return new CS\Memory();
+
+      case 'redis':
+        return new CS\Redis(Redis::database());
+
+      case 'wincache':
+        return new CS\WinCache($conf['cache']['key']);
+
+      case 'dba':
+        return new CS\Dba(String::ensureTrailing('/', $conf['cache']['storage_path']) . $conf['cache']['key']);
+
+      default:
+        throw new \RuntimeException("Cache storage {$storage} is not supported.");
+    }
+  }
+
+  /**
+   * Magic Method for calling the methods on the default cache storage.
+   *
+   * @param $method
+   * @param $parameters
+   *
+   * @return mixed
+   */
+  public static function __callStatic($method, $parameters)
+  {
+    return call_user_func_array(
+      array(static::storage(), $method), $parameters
+    );
+  }
+}

+ 110 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Apc.php

@@ -0,0 +1,110 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Apc extends Storage
+{
+  /**
+   * The cache key from the cache configuration file.
+   *
+   * @var string
+   */
+  protected $key;
+
+  /**
+   * Is APCu is supported.
+   *
+   * @var bool
+   */
+  protected $apcu = false;
+
+  /**
+   * Create a new APC cache storage instance.
+   *
+   * @param string $key
+   */
+  public function __construct($key)
+  {
+    $this->key  = (string)$key;
+    $this->apcu = function_exists('apcu_fetch');
+  }
+
+  /**
+   * Retrieve an item from the cache storage.
+   *
+   * @param string $key
+   *
+   * @return mixed
+   */
+  protected function retrieve($key)
+  {
+    return $this->apcu ? apcu_fetch($this->key . $key) : apc_fetch($this->key . $key);
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   *
+   * @return bool
+   */
+  public function put($key, $value, $minutes)
+  {
+    return $this->apcu
+      ? apcu_store('' . $this->key . $key, $value, (int)$minutes * 60)
+      : apc_store('' . $this->key . $key, $value, (int)$minutes * 60
+      );
+  }
+
+  /**
+   * Write an item to the cache that lasts forever.
+   *
+   * @param  string $key
+   * @param  mixed  $value
+   *
+   * @return boolean|null
+   */
+  public function forever($key, $value)
+  {
+    return $this->put($key, $value, 0);
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   *
+   * @return bool
+   */
+  public function forget($key)
+  {
+    return $this->apcu ? apcu_delete($key) : apc_delete($key);
+  }
+
+  /**
+   * Remove all items from the cache.
+   *
+   * @return void
+   */
+  public function flush()
+  {
+    $this->apcu ? apcu_clear_cache() : apc_clear_cache('user');
+  }
+}

+ 240 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Dba.php

@@ -0,0 +1,240 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * This class provides the functionality required to store
+ * and retrieve PHP strings, integers or arrays.
+ *
+ * It uses the database (dbm-style) abstraction layer for persistence.
+ * Even instances of SimpleXMLElement can be stored. You don't have
+ * to matter about the size of the cache-file. It depends on the free
+ * space of your disk.
+ *
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Dba extends Storage
+{
+  /**
+   * @var resource
+   */
+  protected $dba;
+
+  /**
+   * @var resource
+   */
+  protected $handler;
+
+  /**
+   * @var string
+   */
+  protected $file;
+
+  /**
+   * @param string  $file    the cache-file.
+   *
+   * @param string  $handler the dba handler.
+   *
+   * You have to install one of this handlers before use.
+   *
+   * cdb      = Tiny Constant Database - for reading.
+   * cdb_make = Tiny Constant Database - for writing.
+   * db4      = Oracle Berkeley DB 4   - for reading and writing.
+   * qdbm     = Quick Database Manager - for reading and writing.
+   * gdbm     = GNU Database Manager   - for reading and writing.
+   * flatfile = default dba extension  - for reading and writing.
+   *
+   * Use flatfile-handler only when you cannot install one,
+   * of the libraries required by the other handlers,
+   * and when you cannot use bundled cdb handler.
+   *
+   * @param string  $mode    For read/write access, database creation if it doesn't currently exist.
+   *
+   * @param boolean $persistently
+   *
+   * @throws \RuntimeException If no DBA extension or handler installed.
+   */
+  public function __construct($file, $handler = 'flatfile', $mode = 'c', $persistently = true)
+  {
+    if (false === extension_loaded('dba')) {
+      throw new \RuntimeException('The DBA extension is required for this wrapper, but the extension is not loaded');
+    }
+
+    if (false === in_array($handler, dba_handlers(false))) {
+      throw new \RuntimeException('The ' . $handler . ' handler is required for the DBA extension, but the handler is not installed');
+    }
+
+    $this->dba = (true === $persistently) ? dba_popen($file, $mode, $handler) : dba_open($file, $mode, $handler);
+
+    $this->file    = $file;
+    $this->handler = $handler;
+  }
+
+  /**
+   * Closes an open dba resource
+   *
+   * @return void
+   */
+  public function __destruct()
+  {
+    if ($this->dba) {
+      @dba_close($this->dba);
+      $this->dba = null;
+    }
+  }
+
+  /**
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   *
+   * @return bool
+   */
+  public function put($key, $value, $minutes)
+  {
+    if ($minutes <= 0) {
+      return;
+    }
+
+    $value = $this->expiration($minutes) . serialize($value);
+
+    if (true === $this->has($key)) {
+      return dba_replace($key, $value, $this->dba);
+    }
+
+    return dba_insert($key, $value, $this->dba);
+  }
+
+  /**
+   * @param string $key
+   * @param null $default
+   *
+   * @return bool|mixed|null
+   */
+  public function get($key, $default = null)
+  {
+    $res = $this->retrieve($key);
+
+    if (false === $res) {
+      $this->forget($key);
+
+      return false;
+    }
+
+    return $res;
+  }
+
+  /**
+   * @param string $key
+   *
+   * @return bool|mixed
+   */
+  protected function retrieve($key)
+  {
+    $value = dba_fetch($key, $this->dba);
+
+    if (false === $value) {
+      return false;
+    }
+
+    // compare the timestamp to the current time when we read the value.
+    if (time() >= substr($value, 0, 10)) {
+      return $this->forget($key);
+    }
+
+    return unserialize(substr($value, 10));
+  }
+
+  /**
+   * @param string $key
+   *
+   * @return boolean
+   */
+  public function forget($key)
+  {
+    if (false === is_resource($this->dba)) {
+      return false;
+    }
+
+    return dba_delete($key, $this->dba);
+  }
+
+  /**
+   * @param string $key
+   *
+   * @return boolean
+   */
+  public function has($key)
+  {
+    return dba_exists($key, $this->dba);
+  }
+
+  /**
+   * Write an item to the cache for five years.
+   *
+   * @param $key
+   * @param $value
+   *
+   * @return boolean
+   */
+  public function forever($key, $value)
+  {
+    return $this->put($key, $value, 2628000);
+  }
+
+  /**
+   * Cleans and optimizes the cache from all expired entries.
+   *
+   * @return bool
+   */
+  public function clean()
+  {
+    $dba = $this->dba;
+    $key = dba_firstkey($dba);
+
+    while ($key !== false && $key !== null) {
+      $this->retrieve($key);
+      $key = dba_nextkey($dba);
+    }
+
+    return dba_optimize($dba);
+  }
+
+  /**
+   * Flush the whole storage.
+   *
+   * @return bool
+   */
+  public function flush()
+  {
+    if (file_exists($this->file)) {
+
+      // We close the dba file before deleting
+      // and reopen on next use.
+      $this->__destruct();
+
+      @unlink($this->file);
+
+      clearstatcache();
+
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * @return string
+   */
+  public function getFile()
+  {
+    return $this->file;
+  }
+}

+ 107 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/File.php

@@ -0,0 +1,107 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class File extends Storage
+{
+  /**
+   * The path to which the cache files should be written.
+   *
+   * @var string
+   */
+  protected $path;
+
+  /**
+   * @param $path
+   */
+  public function __construct($path)
+  {
+    $this->path = $path;
+  }
+
+  /**
+   * @param string $key
+   *
+   * @return bool|mixed|null
+   */
+  protected function retrieve($key)
+  {
+    if (!file_exists($this->path . $key)) {
+      return null;
+    }
+
+    // compare the timestamp to the current time when we read the file.
+    if (time() >= substr($cache = file_get_contents($this->path . $key), 0, 10)) {
+      return $this->forget($key);
+    }
+
+    return unserialize(substr($cache, 10));
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   *
+   * @return int|void
+   */
+  public function put($key, $value, $minutes)
+  {
+    if ($minutes <= 0) {
+      return;
+    }
+
+    $value = $this->expiration($minutes) . serialize($value);
+
+    return file_put_contents($this->path . $key, $value, LOCK_EX);
+  }
+
+  /**
+   * Write an item to the cache for five years.
+   *
+   * @param $key
+   * @param $value
+   *
+   * @return int|void
+   */
+  public function forever($key, $value)
+  {
+    return $this->put($key, $value, 2628000);
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   *
+   * @return bool|void
+   */
+  public function forget($key)
+  {
+    if (file_exists($this->path . $key)) {
+
+      @unlink($this->path . $key);
+
+      clearstatcache();
+
+      return true;
+    }
+  }
+}

+ 96 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Memcached.php

@@ -0,0 +1,96 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Memcached extends Storage
+{
+  /**
+   * The Memcache instance.
+   *
+   * @var \Memcached
+   */
+  public $memcache;
+
+  /**
+   * The cache key from the cache configuration file.
+   *
+   * @var string
+   */
+  protected $key;
+
+  /**
+   * @param \Memcached $memcache
+   * @param            $key
+   */
+  public function __construct(\Memcached $memcache, $key)
+  {
+    $this->key      = $key;
+    $this->memcache = $memcache;
+  }
+
+  /**
+   * @param string $key
+   *
+   * @return mixed
+   */
+  protected function retrieve($key)
+  {
+    if (($cache = $this->memcache->get($this->key . $key)) !== false) {
+      return $cache;
+    }
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   *
+   * @return bool|void
+   */
+  public function put($key, $value, $minutes)
+  {
+    return $this->memcache->set($this->key . $key, $value, $minutes * 60);
+  }
+
+  /**
+   * Write an item to the cache that lasts forever.
+   *
+   * @param $key
+   * @param $value
+   *
+   * @return bool|void
+   */
+  public function forever($key, $value)
+  {
+    return $this->put($key, $value, 0);
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   *
+   * @return bool|void
+   */
+  public function forget($key)
+  {
+    return $this->memcache->delete($this->key . $key);
+  }
+}

+ 85 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Memory.php

@@ -0,0 +1,85 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Memory extends Storage
+{
+  /**
+   * The in-memory array of cached items.
+   *
+   * @var array
+   */
+  public $storage = array();
+
+  /**
+   * Retrieve an item from the cache storage.
+   *
+   * @param string $key
+   *
+   * @return mixed|null
+   */
+  protected function retrieve($key)
+  {
+    if (array_key_exists($key, $this->storage)) {
+      return $this->storage[$key];
+    }
+
+    return null;
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   */
+  public function put($key, $value, $minutes)
+  {
+    $this->storage[$key] = $value;
+  }
+
+  /**
+   * Write an item to the cache that lasts forever.
+   *
+   * @param $key
+   * @param $value
+   */
+  public function forever($key, $value)
+  {
+    $this->put($key, $value, 0);
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   */
+  public function forget($key)
+  {
+    unset($this->storage[$key]);
+  }
+
+  /**
+   * Flush the entire cache.
+   */
+  public function flush()
+  {
+    $this->storage = array();
+  }
+}

+ 136 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Pdo.php

@@ -0,0 +1,136 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Pdo extends Storage
+{
+  /**
+   * The cache key from the cache configuration file.
+   *
+   * @var string
+   */
+  protected $key;
+
+  /**
+   * @var \Pimf\Database
+   */
+  protected $pdo;
+
+  /**
+   * Create a new database cache storage instance.
+   *
+   * @param \Pimf\Database $pdo
+   * @param string $key
+   */
+  public function __construct(\Pimf\Database $pdo, $key)
+  {
+    $this->pdo = $pdo;
+    $this->key = (string)$key;
+  }
+
+  /**
+   * Retrieve an item from the cache storage.
+   *
+   * @param string $key
+   *
+   * @return mixed|void
+   */
+  protected function retrieve($key)
+  {
+    $sth = $this->pdo->prepare(
+      'SELECT * FROM pimf_cache WHERE key = :key'
+    );
+
+    $sth->bindValue(':key', $this->key . $key);
+    $sth->execute();
+
+    $cache = $sth->fetchObject();
+
+    if ($cache instanceof \stdClass) {
+
+      if (time() >= $cache->expiration) {
+        return $this->forget($key);
+      }
+
+      return unserialize($cache->value);
+    }
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param  string $key
+   * @param  mixed  $value
+   * @param  int    $minutes
+   *
+   * @return bool
+   */
+  public function put($key, $value, $minutes)
+  {
+    $key        = $this->key . $key;
+    $value      = serialize($value);
+    $expiration = $this->expiration($minutes);
+
+    try {
+      $sth = $this->pdo->prepare(
+        "INSERT INTO pimf_cache (key, value, expiration) VALUES (:key, :value, :expiration)"
+      );
+    } catch (\Exception $exception) {
+      $sth = $this->pdo->prepare(
+        "UPDATE pimf_cache SET value = :value, expiration = :expiration WHERE key = :key"
+      );
+    }
+
+    $sth->bindValue(':key', $key);
+    $sth->bindValue(':value', $value);
+    $sth->bindValue(':expiration', $expiration);
+
+    return $sth->execute();
+  }
+
+  /**
+   * Write an item to the cache for five years.
+   *
+   * @param $key
+   * @param $value
+   *
+   * @return bool
+   */
+  public function forever($key, $value)
+  {
+    return $this->put($key, $value, 2628000);
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   *
+   * @return boolean
+   */
+  public function forget($key)
+  {
+    $sth = $this->pdo->prepare(
+      "DELETE FROM pimf_cache WHERE key = :key"
+    );
+
+    $sth->bindValue(':key', $this->key . $key);
+
+    return $sth->execute();
+  }
+}

+ 98 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Redis.php

@@ -0,0 +1,98 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * Redis usage
+ *
+ * <code>
+ *    // Put an item in the cache for 15 minutes
+ *    Cache::put('name', 'Robin', 15);
+ * </code>
+ *
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Redis extends Storage
+{
+  /**
+   * The Redis database instance.
+   *
+   * @var \Pimf\Redis
+   */
+  protected $redis;
+
+  /**
+   * @param \Pimf\Redis $redis
+   */
+  public function __construct(\Pimf\Redis $redis)
+  {
+    $this->redis = $redis;
+  }
+
+  /**
+   * Determine if an item exists in the cache.
+   *
+   * @param string $key
+   *
+   * @return bool
+   */
+  public function has($key)
+  {
+    return ($this->redis->get($key) !== null);
+  }
+
+  /**
+   * Retrieve an item from the cache storage.
+   *
+   * @param string $key
+   *
+   * @return mixed
+   */
+  protected function retrieve($key)
+  {
+    if (!is_null($cache = $this->redis->get($key))) {
+      return unserialize($cache);
+    }
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   */
+  public function put($key, $value, $minutes)
+  {
+    $this->forever($key, $value);
+    $this->redis->expire($key, $minutes * 60);
+  }
+
+  /**
+   * Write an item to the cache that lasts forever.
+   *
+   * @param string $key
+   * @param $value
+   */
+  public function forever($key, $value)
+  {
+    $this->redis->set($key, serialize($value));
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   */
+  public function forget($key)
+  {
+    $this->redis->del($key);
+  }
+}

+ 137 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Storage.php

@@ -0,0 +1,137 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Storage
+{
+  /**
+   * Determine if an item exists in the cache.
+   *
+   * @param $key
+   *
+   * @return bool
+   */
+  public function has($key)
+  {
+    return ($this->get($key) !== null);
+  }
+
+  /**
+   * Get an item from the cache.
+   *
+   * <code>
+   *    // Get an item from the cache storage
+   *    $name = Cache::storage('name');
+   *
+   *    // Return a default value if the requested item isn't cached
+   *    $name = Cache::get('name', 'Robin');
+   * </code>
+   *
+   * @param      $key
+   * @param null $default
+   *
+   * @return mixed|null
+   */
+  public function get($key, $default = null)
+  {
+    return (!is_null($item = $this->retrieve($key))) ? $item : $default;
+  }
+
+  /**
+   * Retrieve an item from the cache storage.
+   *
+   * @param string $key
+   *
+   * @return mixed
+   */
+  abstract protected function retrieve($key);
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   *
+   * @return void
+   */
+  abstract public function put($key, $value, $minutes);
+
+  /**
+   * Get an item from the cache, or cache and return the default value.
+   *
+   * <code>
+   *    // Get an item from the cache, or cache a value for 15 minutes
+   *    $name = Cache::remember('name', 'Robin', 15);
+   *
+   *    // Use a closure for deferred execution
+   *    $count = Cache::remember('count', function () { return User::count(); }, 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $default
+   * @param int    $minutes
+   * @param string $function
+   *
+   * @return mixed
+   */
+  public function remember($key, $default, $minutes, $function = 'put')
+  {
+    if (!is_null($item = $this->get($key, null))) {
+      return $item;
+    }
+
+    $this->$function($key, $default, $minutes);
+
+    return $default;
+  }
+
+  /**
+   * Get an item from the cache, or cache the default value forever.
+   *
+   * @param string $key
+   * @param mixed  $default
+   *
+   * @return mixed
+   */
+  public function sear($key, $default)
+  {
+    return $this->remember($key, $default, null, 'forever');
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   *
+   * @return boolean
+   */
+  abstract public function forget($key);
+
+  /**
+   * Get the expiration time as a UNIX timestamp.
+   *
+   * @param int $minutes
+   *
+   * @return int
+   */
+  protected function expiration($minutes)
+  {
+    return time() + ($minutes * 60);
+  }
+}

+ 89 - 0
php-pimf/pimf-framework/core/Pimf/Cache/Storages/Wincache.php

@@ -0,0 +1,89 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cache\Storages;
+
+/**
+ * @package Cache_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Wincache extends Storage
+{
+  /**
+   * The cache key from the cache configuration file.
+   *
+   * @var string
+   */
+  protected $key;
+
+  /**
+   * @param $key
+   */
+  public function __construct($key)
+  {
+    $this->key = $key;
+  }
+
+  /**
+   * Retrieve an item from the cache storage.
+   *
+   * @param string $key
+   *
+   * @return mixed
+   */
+  protected function retrieve($key)
+  {
+    if (($cache = wincache_ucache_get($this->key . $key)) !== false) {
+      return $cache;
+    }
+  }
+
+  /**
+   * Write an item to the cache for a given number of minutes.
+   *
+   * <code>
+   *    // Put an item in the cache for 15 minutes
+   *    Cache::put('name', 'Robin', 15);
+   * </code>
+   *
+   * @param string $key
+   * @param mixed  $value
+   * @param int    $minutes
+   *
+   * @return bool|void
+   */
+  public function put($key, $value, $minutes)
+  {
+    return wincache_ucache_add($this->key . $key, $value, $minutes * 60);
+  }
+
+  /**
+   * Write an item to the cache that lasts forever.
+   *
+   * @param $key
+   * @param $value
+   *
+   * @return bool|void
+   */
+  public function forever($key, $value)
+  {
+    return $this->put($key, $value, 0);
+  }
+
+  /**
+   * Delete an item from the cache.
+   *
+   * @param string $key
+   *
+   * @return bool|void
+   */
+  public function forget($key)
+  {
+    return wincache_ucache_delete($this->key . $key);
+  }
+}

+ 143 - 0
php-pimf/pimf-framework/core/Pimf/Cli.php

@@ -0,0 +1,143 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Cli\Color;
+use Pimf\Util\String;
+
+/**
+ * A full featured package for managing command-line options and arguments,
+ * it allows the developer to easily build complex command line interfaces.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+final class Cli
+{
+  /**
+   * Prints out a list of CLI commands from the system,
+   * which is defined at the controllers with the "CliAction()" suffix at the method-name.
+   *
+   * @param string $appClr  Path to application controller repository
+   * @param string $coreClr Path to core controller repository
+   * @param string $root    Path to home directory
+   */
+  public static function absorb($appClr = null, $coreClr = null, $root = null)
+  {
+    echo Color::paint(
+      PHP_EOL . 'PIMF v' . \Pimf\Application::VERSION . ' PHP Command Line Interface by Gjero Krsteski' . PHP_EOL
+    );
+
+    echo Color::paint(
+      '+------------------------------------------------------+' . PHP_EOL
+    );
+
+    self::reflect(self::collect($appClr, $coreClr, $root));
+  }
+
+  /**
+   * @param array $classes
+   */
+  public static function reflect(array $classes)
+  {
+    array_map(
+      function ($class) {
+
+        $reflection = new \ReflectionClass($class);
+
+        if ($reflection->isSubclassOf('\Pimf\Controller\Base')) {
+
+          $methods    = $reflection->getMethods();
+          $controller = explode('_', $class);
+
+          echo Color::paint('controller: ' . strtolower(end($controller)) . '' . PHP_EOL);
+
+          array_map(
+            function ($method) {
+              if (false !== $command = strstr($method->getName(), 'CliAction', true)) {
+                echo Color::paint(PHP_EOL . ' action: ' . $command . ' ' . PHP_EOL);
+              }
+            }, $methods
+          );
+
+          echo Color::paint(
+            PHP_EOL . '+------------------------------------------------------+' . PHP_EOL
+          );
+
+        }
+
+      }, $classes
+    );
+  }
+
+  /**
+   * @param string $appClr
+   * @param string $coreClr
+   * @param string $root
+   *
+   * @return array
+   */
+  public static function collect($appClr = null, $coreClr = null, $root = null)
+  {
+    $classes = array();
+    $conf    = Registry::get('conf');
+    $dis     = DIRECTORY_SEPARATOR;
+
+    if (!$root && !$coreClr && !$appClr) {
+      // compute the PIMF framework path restriction.
+      $root    = dirname(dirname(dirname(dirname(__FILE__))));
+      $coreClr = str_replace('/', $dis, $root . '/pimf-framework/core/Pimf/Controller/');
+      $appClr  = str_replace('/', $dis, $root . '/app/' . $conf['app']['name'] . '/Controller/');
+    }
+
+    foreach (array($appClr, $coreClr) as $dir) {
+
+      $iterator
+        = new \RegexIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)), '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
+
+      foreach (iterator_to_array($iterator, false) as $file) {
+        $file = str_replace("\\", '/', current($file));
+        $file = str_replace('/', $dis, $file);
+        $name = str_replace(
+          array($root . $dis . 'pimf-framework' . $dis . 'core' . $dis, $root . $dis . 'app' . $dis), '', $file
+        );
+
+        $name      = str_replace($dis, '\\', $name);
+        $name      = str_replace('.php', '', $name);
+        $classes[] = '\\' . $name;
+      }
+    }
+
+    return $classes;
+  }
+
+  /**
+   * @param array $commands
+   *
+   * @return array
+   */
+  public static function parse(array $commands)
+  {
+    $cli = array();
+
+    parse_str(implode('&', array_slice($commands, 1)), $cli);
+
+    $command = current(array_keys((array)$cli, ''));
+
+    if (String::contains($command, ':')) {
+
+      list($controller, $action) = explode(':', $command);
+
+      $cli['controller'] = $controller;
+      $cli['action']     = $action;
+    }
+
+    return $cli;
+  }
+}

+ 81 - 0
php-pimf/pimf-framework/core/Pimf/Cli/Color.php

@@ -0,0 +1,81 @@
+<?php
+/**
+ * Cli
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cli;
+
+use Pimf\Sapi;
+
+/**
+ * For easily use ANSI console colors in your application.
+ *
+ * @package Cli
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Color
+{
+  protected static $foreground = array(
+      'black'        => '0;30',
+      'dark_gray'    => '1;30',
+      'blue'         => '0;34',
+      'light_blue'   => '1;34',
+      'green'        => '0;32',
+      'light_green'  => '1;32',
+      'cyan'         => '0;36',
+      'light_cyan'   => '1;36',
+      'red'          => '0;31',
+      'light_red'    => '1;31',
+      'purple'       => '0;35',
+      'light_purple' => '1;35',
+      'brown'        => '0;33',
+      'yellow'       => '1;33',
+      'light_gray'   => '0;37',
+      'white'        => '1;37',
+    );
+
+  protected static $background = array(
+      'black'      => '40',
+      'red'        => '41',
+      'green'      => '42',
+      'yellow'     => '43',
+      'blue'       => '44',
+      'magenta'    => '45',
+      'cyan'       => '46',
+      'light_gray' => '47',
+    );
+
+  /**
+   * Returns colored string
+   *
+   * @param string      $string
+   * @param string|null $foregroundColor
+   * @param null        $backgroundColor
+   *
+   * @return string
+   */
+  public static function paint($string, $foregroundColor = 'cyan', $backgroundColor = null)
+  {
+    if (Sapi::isWindows()) {
+      return $string;
+    }
+
+    $colored = "";
+
+    // check if given foreground color found
+    if (isset(self::$foreground[$foregroundColor])) {
+      $colored .= "\033[" . self::$foreground[$foregroundColor] . "m";
+    }
+
+    // check if given background color found
+    if (isset(self::$background[$backgroundColor])) {
+      $colored .= "\033[" . static::$background[$backgroundColor] . "m";
+    }
+
+    // add string and end coloring
+    return $colored . $string . "\033[0m";
+  }
+}

+ 105 - 0
php-pimf/pimf-framework/core/Pimf/Cli/Std.php

@@ -0,0 +1,105 @@
+<?php
+/**
+ * Cli
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Cli;
+
+/**
+ * Responsible for accessing I/O streams that allow access to PHP's own input and output streams.
+ *
+ * @package Cli
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Std
+{
+  const READ = 'php://stdin';
+
+  /**
+   * @var resource
+   */
+  private $handle;
+
+  /**
+   * @param string $stream
+   */
+  public function __construct($stream = self::READ)
+  {
+    $this->handle = fopen($stream, 'r');
+  }
+
+  /**
+   * @return string
+   */
+  public function value()
+  {
+    return substr(fgets($this->handle, 1024), 0, -1);
+  }
+
+  public function __destruct()
+  {
+    fclose($this->handle);
+  }
+
+  /**
+   * Allow direct access to the corresponding input stream of the PHP process.
+   *
+   * @param string $prompt
+   * @param string $validation A regex pattern
+   *
+   * <code>
+   *
+   *  Have a look at the examples for $validation:
+   *
+   *  Regular Expression  | Will match...
+   *  -------------------------------------------------------------
+   *  .*                  | Not empty
+   *  foo                 | The string "foo"
+   *  ^foo                | "foo" at the start of a string
+   *  foo$                | "foo" at the end of a string
+   *  ^foo$               | "foo" when it is alone on a string
+   *  [abc]               | a, b, or c
+   *  [a-z]               | Any lowercase letter
+   *  [^A-Z]              | Any character that is not a uppercase letter
+   *  (gif|jpg)           | Matches either "gif" or "jpeg"
+   *  [a-z]+              | One or more lowercase letters
+   *  [0-9\.\-]           | Аny number, dot, or minus sign
+   *
+   * </code>
+   *
+   * @return string
+   */
+  public function read($prompt, $validation = "/.*/")
+  {
+    $value = '';
+
+    while (true) {
+
+      echo Color::paint("Please enter a " . $prompt . ":\n");
+
+      $value = $this->value();
+
+      if ($this->valid($validation, $value)) {
+        break;
+      }
+
+      echo Color::paint("Value format for " . $prompt . " is invalid!\n", 'red');
+    }
+
+    return $value;
+  }
+
+  /**
+   * @param string $validation A regex pattern
+   * @param string $value
+   *
+   * @return bool
+   */
+  public function valid($validation, $value)
+  {
+    return strlen($value) > 0 && preg_match($validation, $value);
+  }
+}

+ 26 - 0
php-pimf/pimf-framework/core/Pimf/Contracts/Arrayable.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Contracts;
+
+/**
+ * A simply interface to get instance as an array.
+ *
+ * @package Contracts
+ * @author  Gjero Krsteski <[email protected]>
+ */
+interface Arrayable
+{
+  /**
+   * Get the instance as an array.
+   *
+   * @return array
+   */
+  public function toArray();
+
+}

+ 28 - 0
php-pimf/pimf-framework/core/Pimf/Contracts/Cleanable.php

@@ -0,0 +1,28 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Contracts;
+
+/**
+ * A simply interface to delete all expired data from persistent storage of the instance.
+ *
+ * @package Contracts
+ * @author  Gjero Krsteski <[email protected]>
+ */
+interface Cleanable
+{
+  /**
+   * Delete all expired instance-data from persistent storage.
+   *
+   * @param int $expiration
+   *
+   * @return mixed
+   */
+  public function clean($expiration);
+
+}

+ 28 - 0
php-pimf/pimf-framework/core/Pimf/Contracts/Jsonable.php

@@ -0,0 +1,28 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Contracts;
+
+/**
+ * A simply interface to get instance to its JSON representation.
+ *
+ * @package Contracts
+ * @author  Gjero Krsteski <[email protected]>
+ */
+interface Jsonable
+{
+  /**
+   * Convert the object to its JSON representation.
+   *
+   * @param  int $options
+   *
+   * @return string
+   */
+  public function toJson($options = 0);
+
+}

+ 26 - 0
php-pimf/pimf-framework/core/Pimf/Contracts/MessageProvider.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Contracts;
+
+/**
+ * A simply interface to get messages for the instance.
+ *
+ * @package Contracts
+ * @author  Gjero Krsteski <[email protected]>
+ */
+interface MessageProvider
+{
+  /**
+   * Get the messages for the instance.
+   *
+   * @return \Pimf\Util\Message[]
+   */
+  public function getMessages();
+
+}

+ 26 - 0
php-pimf/pimf-framework/core/Pimf/Contracts/Renderable.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Contracts;
+
+/**
+ * A simply interface to give the view-adapters teh contents of the object.
+ *
+ * @package Contracts
+ * @author  Gjero Krsteski <[email protected]>
+ */
+interface Renderable
+{
+  /**
+   * Get the evaluated contents of the object.
+   *
+   * @return string
+   */
+  public function render();
+
+}

+ 26 - 0
php-pimf/pimf-framework/core/Pimf/Contracts/Reunitable.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Contracts;
+
+/**
+ * A simply interface to give the view-adapters ro re-unite the template an the variables.
+ *
+ * @package Contracts
+ * @author  Gjero Krsteski <[email protected]>
+ */
+interface Reunitable
+{
+  /**
+   * Puts the template an the variables together.
+   *
+   * @throws \Exception
+   * @return string
+   */
+  public function reunite();
+}

+ 115 - 0
php-pimf/pimf-framework/core/Pimf/Controller/Base.php

@@ -0,0 +1,115 @@
+<?php
+/**
+ * Controller
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Controller;
+
+use \Pimf\Param, \Pimf\Registry, \Pimf\Sapi, \Pimf\Controller\Exception as Bomb, \Pimf\Request, \Pimf\Util\Header, \Pimf\Url,
+  \Pimf\Response;
+
+/**
+ * Defines the general controller behaviour - you have to extend it.
+ *
+ * @package Controller
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Base
+{
+  /**
+   * @var \Pimf\Request
+   */
+  protected $request;
+
+  /**
+   * @var \Pimf\Response
+   */
+  protected $response;
+
+  /**
+   * @param Request  $request
+   * @param Response $response
+   */
+  public function __construct(\Pimf\Request $request, \Pimf\Response $response = null)
+  {
+    $this->request  = $request;
+    $this->response = $response;
+  }
+
+  abstract public function indexAction();
+
+  /**
+   * Method to show the content.
+   *
+   * @return mixed
+   * @throws \Exception If not supported request method or bad controller
+   */
+  public function render()
+  {
+    $conf = Registry::get('conf');
+
+    if (Sapi::isCli() && $conf['environment'] == 'production') {
+
+      $suffix = 'CliAction';
+      $action = $this->request->fromCli()->get('action') ? : 'index';
+
+    } else {
+
+      $requestMethod = ucfirst(Registry::get('env')->REQUEST_METHOD);
+      $suffix        = 'Action';
+
+      if (!method_exists($this->request, $bag = 'from' . $requestMethod)) {
+        throw new Bomb("not supported request method=" . $requestMethod);
+      }
+
+      $action = $this->request->{$bag}()->get('action') ? : 'index';
+
+      if ($conf['app']['routeable'] === true) {
+
+        $target = Registry::get('router')->find();
+
+        if ($target instanceof \Pimf\Route\Target) {
+
+          $action = $target->getAction();
+
+          Request::$getData = new Param((array)Request::stripSlashesIfMagicQuotes(
+            array_merge($target->getParams(), Request::$getData->getAll())
+          ));
+        }
+      }
+    }
+
+    $action = strtolower($action) . $suffix;
+
+    if (method_exists($this, 'init')) {
+      call_user_func(array($this, 'init'));
+    }
+
+    if (!method_exists($this, $action)) {
+      throw new Bomb("no action '{$action}' defined at controller " . get_class($this));
+    }
+
+    return call_user_func(array($this, $action));
+  }
+
+  /**
+   * Prepares the response object to return an HTTP Redirect response to the client.
+   *
+   * @param string  $route     The redirect destination like controller/action
+   * @param boolean $permanent If permanent redirection or not.
+   * @param boolean $exit
+   */
+  public function redirect($route, $permanent = false, $exit = true)
+  {
+    $url = Url::compute($route);
+
+    Header::clear();
+
+    ($permanent === true) ? Header::sendMovedPermanently() : Header::sendFound();
+
+    Header::toLocation($url, $exit);
+  }
+}

+ 135 - 0
php-pimf/pimf-framework/core/Pimf/Controller/Core.php

@@ -0,0 +1,135 @@
+<?php
+/**
+ * Controller
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Controller;
+
+use Pimf\Registry, Pimf\Util\String, Pimf\Cli\Color, Pimf\Cli\Std, Pimf\Pdo\Factory, \Pimf\Controller\Exception as Bomb, Pimf\Util\File;
+
+/**
+ * @package Controller
+ * @author  Gjero Krsteski <[email protected]>
+ * @codeCoverageIgnore
+ */
+class Core extends Base
+{
+  /**
+   * Because it is a PIMF restriction!
+   */
+  public function indexAction()
+  {
+
+  }
+
+  /**
+   * Checks the applications architecture and creates some security and safety measures.
+   */
+  public function initCliAction()
+  {
+    clearstatcache();
+
+    $conf = Registry::get('conf');
+    $app  = 'app/' . $conf['app']['name'] . '/';
+
+    $assets = array(BASE_PATH . $app . '_session/', BASE_PATH . $app . '_cache/', BASE_PATH . $app . '_database/',
+                    BASE_PATH . $app . '_templates/',);
+
+    echo Color::paint('Check app assets' . PHP_EOL);
+
+    foreach ($assets as $asset) {
+
+      if (!is_dir($asset)) {
+        echo Color::paint("Please create '$asset' directory! " . PHP_EOL, 'red');
+      }
+
+      if (!is_writable($asset)) {
+        echo Color::paint("Please make '$asset' writable! " . PHP_EOL, 'red');
+      }
+    }
+
+    echo Color::paint('Secure root directory' . PHP_EOL);
+    chmod(BASE_PATH, 0755);
+
+    echo Color::paint('Secure .htaccess' . PHP_EOL);
+    chmod(BASE_PATH . '.htaccess', 0644);
+
+    echo Color::paint('Secure index.php' . PHP_EOL);
+    chmod(BASE_PATH . 'index.php', 0644);
+
+    echo Color::paint('Secure autoload.core.php' . PHP_EOL);
+    chmod(BASE_PATH . 'pimf-framework/autoload.core.php', 0644);
+
+    echo Color::paint('Create logging files' . PHP_EOL);
+    $handle = fopen($file = $conf['bootstrap']['local_temp_directory'] . 'pimf-logs.txt', "at+");
+    fclose($handle);
+    chmod($file, 0777);
+    $handle = fopen($file = $conf['bootstrap']['local_temp_directory'] . 'pimf-warnings.txt', "at+");
+    fclose($handle);
+    chmod($file, 0777);
+    $handle = fopen($file = $conf['bootstrap']['local_temp_directory'] . 'pimf-errors.txt', "at+");
+    fclose($handle);
+    chmod($file, 0777);
+
+    clearstatcache();
+  }
+
+  public function create_session_tableCliAction()
+  {
+    $std = new Std();
+    $type = $std->read('database type [mysql|sqlite]', '(mysql|sqlite)');
+
+    var_dump(
+      $this->createTable($type, 'session')
+    );
+  }
+
+  public function create_cache_tableCliAction()
+  {
+    $std = new Std();
+    $type = $std->read('database type [mysql|sqlite]', '(mysql|sqlite)');
+
+    var_dump(
+      $this->createTable($type, 'cache')
+    );
+  }
+
+  /**
+   * @param string $type
+   * @param string $for
+   *
+   * @return bool
+   * @throws \DomainException
+   */
+  protected function createTable($type, $for)
+  {
+    $type = trim($type);
+
+    try {
+      $pdo = $file = null;
+
+      $conf = Registry::get('conf');
+
+      switch ($for) {
+        case 'cache':
+          $pdo  = Factory::get($conf['cache']['database']);
+          $file = 'create-cache-table-' . $type . '.sql';
+          break;
+        case 'session':
+          $pdo  = Factory::get($conf['session']['database']);
+          $file = 'create-session-table-' . $type . '.sql';
+          break;
+      }
+
+      $file = str_replace('/', DS, BASE_PATH . 'pimf-framework/core/Pimf/_database/' . $file);
+
+      return $pdo->exec(file_get_contents(new File($file))) or print_r($pdo->errorInfo(), true);
+
+    } catch (\PDOException $pdoe) {
+      throw new Bomb($pdoe->getMessage());
+    }
+  }
+}

+ 22 - 0
php-pimf/pimf-framework/core/Pimf/Controller/Exception.php

@@ -0,0 +1,22 @@
+<?php
+/**
+ * Controller
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Controller;
+
+/**
+ * Use this exception when you want slightly to show in a application.
+ *
+ * @package Controller
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @codeCoverageIgnore
+ */
+class Exception extends \DomainException
+{
+
+}

+ 199 - 0
php-pimf/pimf-framework/core/Pimf/Cookie.php

@@ -0,0 +1,199 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Using the cookie
+ *
+ * <code>
+ *    // Get the value of the "favorite" cookie
+ *    $favorite = Cookie::get('favorite');
+ *
+ *    // Get the value of a cookie or return a default value
+ *    $favorite = Cookie::get('framework', 'Pimf');
+ *
+ *    // Set the value of the "favorite" cookie
+ *    Cookie::put('favorite', 'Pimf');
+ *
+ *    // Set the value of the "favorite" cookie for twenty minutes
+ *    Cookie::put('favorite', 'Pimf', 20);
+ *
+ *    // Set a cookie that should last one year
+ *    Cookie::forever('favorite', 'Blue');
+ *
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Cookie
+{
+  /**
+   * How long is forever (in minutes)?
+   *
+   * @var int
+   */
+  const FOREVER = 2628000;
+
+  /**
+   * The cookies that have been set.
+   *
+   * @var array
+   */
+  public static $jar = array();
+
+  /**
+   * Determine if a cookie exists.
+   *
+   * @param  string $name
+   *
+   * @return bool
+   */
+  public static function has($name)
+  {
+    return (static::get($name) !== null);
+  }
+
+  /**
+   * Get the value of a cookie.
+   *
+   * @param      $name
+   * @param null $default
+   *
+   * @return null|string
+   */
+  public static function get($name, $default = null)
+  {
+    if (isset(static::$jar[$name])) {
+      return static::parse(static::$jar[$name]['value']);
+    }
+
+    $cookie = Request::$cookieData;
+
+    if (!is_null($value = $cookie->get($name))) {
+      return static::parse($value);
+    }
+
+    return $default;
+  }
+
+  /**
+   * Set the value of a cookie.
+   *
+   * @param        $name
+   * @param        $value
+   * @param int    $expiration
+   * @param string $path
+   * @param null   $domain
+   * @param bool   $secure
+   *
+   * @return bool
+   * @throws \RuntimeException
+   */
+  public static function put($name, $value, $expiration = 0, $path = '/', $domain = null, $secure = false)
+  {
+    if ($expiration !== 0) {
+      $expiration = time() + ($expiration * 60);
+    }
+
+    $value = static::hash($value) . '+' . $value;
+
+    // If we are attempting to send a secure cookie over the insecure HTTP.
+    $conf = Registry::get('conf');
+
+    if ($secure === true and $conf['ssl'] === false) {
+      throw new \RuntimeException("Attempting to set secure cookie over HTTP!");
+    }
+
+    static::$jar[$name] = compact('name', 'value', 'expiration', 'path', 'domain', 'secure');
+
+    return true;
+  }
+
+  /**
+   * Set a "permanent" cookie. The cookie will last for one year.
+   *
+   * @param        $name
+   * @param        $value
+   * @param string $path
+   * @param null   $domain
+   * @param bool   $secure
+   *
+   * @return bool
+   */
+  public static function forever($name, $value, $path = '/', $domain = null, $secure = false)
+  {
+    return static::put($name, $value, static::FOREVER, $path, $domain, $secure);
+  }
+
+  /**
+   * Delete a cookie.
+   *
+   * @param        string $name
+   * @param string $path
+   * @param null   $domain
+   * @param bool   $secure
+   *
+   * @return bool
+   */
+  public static function forget($name, $path = '/', $domain = null, $secure = false)
+  {
+    return static::put($name, null, -2000, $path, $domain, $secure);
+  }
+
+  /**
+   * Hash the given cookie value.
+   *
+   * @param string $value
+   *
+   * @return string
+   */
+  public static function hash($value)
+  {
+    $conf = Registry::get('conf');
+
+    return hash_hmac('sha1', $value, $conf['app']['key']);
+  }
+
+  /**
+   * Parse a hash fingerprinted cookie value.
+   *
+   * @param string $value
+   *
+   * @return string
+   */
+  protected static function parse($value)
+  {
+    $segments = explode('+', $value);
+
+    // check if the cookie is invalid.
+    if (!(count($segments) >= 2)) {
+      return null;
+    }
+
+    $value = implode('+', array_slice($segments, 1));
+
+    // check the SHA-1 hash from the cookie.
+    if ($segments[0] == static::hash($value)) {
+      return $value;
+    }
+
+    return null;
+  }
+
+  /**
+   * Send along with the rest of the HTTP headers.
+   */
+  public static function send()
+  {
+    foreach (static::$jar as $cookie) {
+      setcookie($cookie['name'], $cookie['value'], $cookie['expiration'], $cookie['path'], $cookie['domain'], $cookie['secure'], true);
+    }
+  }
+}

+ 71 - 0
php-pimf/pimf-framework/core/Pimf/DataMapper/Base.php

@@ -0,0 +1,71 @@
+<?php
+/**
+ * DataMapper
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\DataMapper;
+
+use Pimf\Util\IdentityMap;
+
+/**
+ * For mapping the domain models to the persistence layer.
+ *
+ * Defines the general behaviour for the data-mappers - you have to extend it.
+ *
+ * You have to use it if you want to persist data.
+ *
+ * @package DataMapper
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @method insert($entity)
+ * @method update($entity)
+ * @method delete($entity)
+ * @method find($key)
+ */
+abstract class Base
+{
+  /**
+   * @var \PDO The database resource.
+   */
+  protected $pdo;
+
+  /**
+   * @var \Pimf\Util\IdentityMap
+   */
+  protected $identityMap;
+
+  /**
+   * @param \PDO $pdo
+   */
+  public function __construct(\PDO $pdo)
+  {
+    $this->pdo         = $pdo;
+    $this->identityMap = new IdentityMap();
+  }
+
+  public function __destruct()
+  {
+    unset($this->identityMap, $this->pdo);
+  }
+
+  /**
+   * Makes a given model-property accessible.
+   *
+   * @param object $model
+   * @param int    $value
+   * @param string $property
+   *
+   * @return mixed
+   */
+  public function reflect($model, $value, $property = 'id')
+  {
+    $attribute = new \ReflectionProperty($model, $property);
+    $attribute->setAccessible(true);
+    $attribute->setValue($model, $value);
+
+    return $model;
+  }
+}

+ 82 - 0
php-pimf/pimf-framework/core/Pimf/Database.php

@@ -0,0 +1,82 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Database extends \PDO
+{
+  /**
+   * The current transaction level.
+   *
+   * @var int
+   */
+  protected $transLevel = 0;
+
+  /**
+   * Check database drivers that support savepoints.
+   *
+   * @return bool
+   */
+  public function nestable()
+  {
+    return in_array(
+      $this->getAttribute(\PDO::ATTR_DRIVER_NAME), array("pgsql", "mysql")
+    );
+  }
+
+  /**
+   * @return bool|void
+   */
+  public function beginTransaction()
+  {
+    if ($this->transLevel == 0 || !$this->nestable()) {
+      parent::beginTransaction();
+    } else {
+      $this->exec("SAVEPOINT LEVEL{$this->transLevel}");
+    }
+
+    $this->transLevel++;
+  }
+
+  /**
+   * @return bool|void
+   */
+  public function commit()
+  {
+    $this->transLevel--;
+
+    if ($this->transLevel == 0 || !$this->nestable()) {
+      parent::commit();
+    } else {
+      $this->exec("RELEASE SAVEPOINT LEVEL{$this->transLevel}");
+    }
+  }
+
+  /**
+   * @return bool|void
+   * @throws \PDOException
+   */
+  public function rollBack()
+  {
+    if ($this->transLevel == 0) {
+      throw new \PDOException('trying to rollback without a transaction-start', 25000);
+    }
+
+    $this->transLevel--;
+
+    if ($this->transLevel == 0 || !$this->nestable()) {
+      parent::rollBack();
+    } else {
+      $this->exec("ROLLBACK TO SAVEPOINT LEVEL{$this->transLevel}");
+    }
+  }
+}

+ 103 - 0
php-pimf/pimf-framework/core/Pimf/EntityManager.php

@@ -0,0 +1,103 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\DataMapper\Base;
+
+/**
+ * Based on PDO it is a general manager for data persistence and object relational mapping.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ */
+class EntityManager extends Base
+{
+  /**
+   * @var string The namespace name of data-mappers repository.
+   */
+  protected $prefix;
+
+  /**
+   * @param \PDO   $pdo
+   * @param string $prefix The data-mappers repository name.
+   */
+  public function __construct(\PDO $pdo, $prefix = '\Pimf')
+  {
+    parent::__construct($pdo);
+    $this->prefix = $prefix . '\DataMapper\\';
+  }
+
+  /**
+   * @param string $entity The name of the data-mapper class.
+   *
+   * @return Base
+   * @throws \BadMethodCallException If no entity fount at the repository.
+   */
+  public function load($entity)
+  {
+    $entity = $this->prefix . ucfirst($entity);
+
+    if (true === $this->identityMap->hasId($entity)) {
+      return $this->identityMap->getObject($entity);
+    }
+
+    if (!class_exists($entity)) {
+      throw new \BadMethodCallException('entity "' . $entity . '" found at the data-mapper repository');
+    }
+
+    $model = new $entity($this->pdo);
+
+    $this->identityMap->set($entity, $model);
+
+    return $this->identityMap->getObject($entity);
+  }
+
+  /**
+   * @return bool
+   */
+  public function beginTransaction()
+  {
+    return $this->pdo->beginTransaction();
+  }
+
+  /**
+   * @return bool
+   */
+  public function commitTransaction()
+  {
+    return $this->pdo->commit();
+  }
+
+  /**
+   * @return bool
+   */
+  public function rollbackTransaction()
+  {
+    return $this->pdo->rollBack();
+  }
+
+  /**
+   * @param string $entity
+   *
+   * @return Base
+   */
+  public function __get($entity)
+  {
+    return $this->load($entity);
+  }
+
+  /**
+   * @return Database
+   */
+  public function getPDO()
+  {
+    return $this->pdo;
+  }
+}

+ 231 - 0
php-pimf/pimf-framework/core/Pimf/Environment.php

@@ -0,0 +1,231 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf;
+
+/**
+ * Server and execution environment information.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @property string X_REQUESTED_WITH       It is sent by the Ajax functions of most major Frameworks
+ * @property string HTTP                   Is the application running under HTTP protocol?
+ * @property string HTTPS                  Is the application running under HTTPS protocol?
+ * @property string SERVER_PROTOCOL        Name and revision of the information protocol via which the page was requested; i.e. 'HTTP/1.0';
+ * @property string CONTENT_LENGTH         The Content-Length
+ * @property string HOST                   The name of the server host under which the current script is executing.
+ * @property string SERVER_NAME            The name of the server host under which the current script is executing.
+ * @property string SERVER_PORT            Get the port
+ * @property string PHP_SELF               Filename of the currently executing script.
+ * @property string SCRIPT_NAME            Get Script Name (physical path)
+ * @property string PATH_INFO              Get Path Info (virtual path)
+ * @property string X_FORWARDED_FOR        Do on your machine is behind the proxy than us it instead of REMOTE_ADDR
+ * @property string CLIENT_IP              Get the client ip address
+ * @property string REMOTE_ADDR            The IP address from which the user is viewing the current page.
+ * @property string HTTP_REFERER           Get Referer - it cannot really be trusted.
+ * @property string USER_AGENT             Contents of the User-Agent from the current request, if there is one.
+ * @property string HTTP_USER_AGENT        Contents of the User-Agent: header from the current request, if there is one.
+ * @property string REQUEST_URI            The URI which was given in order to access this page; for instance, '/index.html'.
+ * @property string REQUEST_METHOD         Which request method was used to access the page; i.e. 'GET', 'HEAD', 'POST', 'PUT'.
+ * @property string HTTP_IF_MODIFIED_SINCE Get request header from Apache even on PHP running as a CGI
+ * @property string HTTP_IF_NONE_MATCH     Get request header from Apache even on PHP running as a CGI
+ */
+class Environment
+{
+  /**
+   * @var Param
+   */
+  private $envData;
+
+  /**
+   * @param array $envData Like $_SERVER
+   */
+  public function __construct(array $envData)
+  {
+    $this->envData = new Param($envData);
+  }
+
+  /**
+   * @return Param
+   */
+  public function getData()
+  {
+    return $this->envData;
+  }
+
+  /**
+   * @param $key
+   *
+   * @return string
+   */
+  public function __get($key)
+  {
+    return $this->envData->get($key);
+  }
+
+  /**
+   * Is this an AJAX request?
+   *
+   * @return bool
+   */
+  public function isAjax()
+  {
+    return $this->X_REQUESTED_WITH === 'XMLHttpRequest';
+  }
+
+  /**
+   * Is the application running under HTTP protocol?
+   *
+   * @return bool
+   */
+  public function isHttp()
+  {
+    return (bool)$this->HTTP;
+  }
+
+  /**
+   * Is the application running under HTTPS protocol?
+   *
+   * @return bool
+   */
+  public function isHttps()
+  {
+    return $this->HTTPS === 'on';
+  }
+
+  /**
+   * Get Host
+   *
+   * @return string
+   */
+  public function getHost()
+  {
+    if ($this->HOST) {
+
+      if (strpos($this->HOST, ':') !== false) {
+        $hostParts = explode(':', $this->HOST);
+
+        return $hostParts[0];
+      }
+
+      return $this->HOST;
+    }
+
+    return $this->SERVER_NAME;
+  }
+
+  /**
+   * Get Host with Port
+   *
+   * @return string
+   */
+  public function getHostWithPort()
+  {
+    return '' . $this->getHost() . ':' . $this->SERVER_PORT;
+  }
+
+  /**
+   * Physical path + virtual path
+   *
+   * @return string
+   */
+  public function getPath()
+  {
+    return $this->SCRIPT_NAME . $this->PATH_INFO;
+  }
+
+  /**
+   * Get remote IP
+   *
+   * @return string
+   */
+  public function getIp()
+  {
+    if ($this->X_FORWARDED_FOR) {
+      return $this->X_FORWARDED_FOR;
+    }
+
+    if ($this->CLIENT_IP) {
+      return $this->CLIENT_IP;
+    }
+
+    if ($this->SERVER_NAME) {
+      return gethostbyname($this->SERVER_NAME);
+    }
+
+    return $this->REMOTE_ADDR;
+  }
+
+  /**
+   * Get User Agent
+   *
+   * @return string|null
+   */
+  public function getUserAgent()
+  {
+    if ($this->USER_AGENT) {
+      return $this->USER_AGENT;
+    }
+
+    if ($this->HTTP_USER_AGENT) {
+      return $this->HTTP_USER_AGENT;
+    }
+
+    return null;
+  }
+
+  /**
+   * Gives you the current page URL
+   *
+   * @return string
+   */
+  public function getUrl()
+  {
+    $protocol = strpos(strtolower($this->PATH_INFO), 'https') === false ? 'http' : 'https';
+
+    return $protocol . '://' . $this->getHost();
+  }
+
+  /**
+   * Try to get a request header.
+   *
+   * @param string $header
+   *
+   * @return array
+   */
+  public function getRequestHeader($header)
+  {
+    $header = str_replace('-', '_', strtoupper($header));
+    $value  = $this->{'HTTP_' . $header};
+
+    if (!$value) {
+      $headers = $this->getRequestHeaders();
+      $value   = !empty($headers[$header]) ? $headers[$header] : null;
+    }
+
+    return $value;
+  }
+
+  /**
+   * Try to determine all request headers
+   *
+   * @return array
+   */
+  public function getRequestHeaders()
+  {
+    $headers = array();
+
+    foreach ($this->envData->getAll() as $key => $value) {
+      if ('HTTP_' === substr($key, 0, 5)) {
+        $headers[substr($key, 5)] = $value;
+      }
+    }
+
+    return $headers;
+  }
+}

+ 134 - 0
php-pimf/pimf-framework/core/Pimf/Error.php

@@ -0,0 +1,134 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Util\Header;
+
+/**
+ * Defines the default exception handler if an exception is not caught within a try/catch block.
+ * Execution will stop after the exception_handler is called.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Error
+{
+  /**
+   * Handle an exception and display the exception report.
+   *
+   * @param \Exception $exception
+   * @param boolean    $exit
+   */
+  public static function exception(\Exception $exception, $exit = true)
+  {
+    static::log($exception);
+
+    ob_get_length() > 0 and ob_get_level() and ob_end_clean();
+
+    $conf = Registry::get('conf');
+
+    if (isset($conf['error']['debug_info']) && $conf['error']['debug_info'] === true) {
+      echo static::format($exception);
+      if ($exit) {
+        exit;
+      }
+    }
+
+    Header::clear();
+
+    if ($exception instanceof \Pimf\Controller\Exception
+      || $exception instanceof \Pimf\Resolver\Exception
+    ) {
+      Event::first('404', array($exception));
+      Header::sendNotFound(null, $exit);
+    } else {
+      Event::first('500', array($exception));
+      Header::sendInternalServerError(null, $exit);
+    }
+  }
+
+  /**
+   * If detailed errors are enabled, just format the exception into
+   * a simple error message and display it.
+   *
+   * @param \Exception $exception
+   *
+   * @return string
+   */
+  public static function format(\Exception $exception)
+  {
+    if (Sapi::isCli()) {
+      return
+        "+++ Untreated Exception +++" . PHP_EOL . "Message: " . $exception->getMessage() . PHP_EOL . "Location: " . $exception->getFile()
+        . " on line " . $exception->getLine() . PHP_EOL . "Stack Trace: " . PHP_EOL . $exception->getTraceAsString() . PHP_EOL;
+    }
+
+    return "<html><h2>Untreated Exception</h2>
+      <h3>Message:</h3>
+      <pre>" . $exception->getMessage() . "</pre>
+      <h3>Location:</h3>
+      <pre>" . $exception->getFile() . " on line " . $exception->getLine() . "</pre>
+      <h3>Stack Trace:</h3>
+      <pre>" . $exception->getTraceAsString() . "</pre></html>";
+  }
+
+  /**
+   * Handle a native PHP error as an ErrorException.
+   *
+   * @param int    $code
+   * @param string $error
+   * @param string $file
+   * @param int    $line
+   */
+  public static function native($code, $error, $file, $line)
+  {
+    if (error_reporting() === 0) {
+      return;
+    }
+
+    // create an ErrorException for the PHP error
+    $exception = new \ErrorException($error, $code, 0, $file, $line);
+
+    $conf = Registry::get('conf');
+
+    if (in_array($code, (array)$conf['error']['ignore_levels'])) {
+      return static::log($exception);
+    }
+
+    // display the ErrorException
+    static::exception($exception);
+  }
+
+  /**
+   * Handle the PHP shutdown event.
+   */
+  public static function shutdown()
+  {
+    // if a fatal error occurred
+    $error = error_get_last();
+
+    if (!is_null($error)) {
+      static::exception(new \ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
+    }
+  }
+
+  /**
+   * Log an exception.
+   *
+   * @param \Exception $exception
+   */
+  public static function log(\Exception $exception)
+  {
+    $conf = Registry::get('conf');
+
+    if (isset($conf['error']['log']) && $conf['error']['log'] === true) {
+      Registry::get('logger')->error($exception->getMessage() . ' ' . $exception->getTraceAsString());
+    }
+  }
+}

+ 177 - 0
php-pimf/pimf-framework/core/Pimf/Event.php

@@ -0,0 +1,177 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Provides a great way to build de-coupled applications and allows plug-ins to tap
+ * into the core of your application without modifying the code.
+ *
+ * Register a callback for a given event.
+ *
+ * <code>
+ *    // register a callback for the "start" event
+ *    Efs_Event::listen('start', function () {return 'Started!';});
+ *
+ *    // register an object instance callback for the given event
+ *    Efs_Event::listen('event', array($object, 'method'));
+ * </code>
+ *
+ * Fire an event and return the first response.
+ *
+ * <code>
+ *    // fire the "start" event
+ *    $response = Efs_Event::first('start');
+ *
+ *    // fire the "start" event passing an array of parameters
+ *    $response = Efs_Event::first('start', array('Pimf', 'Framework'));
+ * </code>
+ *
+ * Fire an event so that all listeners are called.
+ *
+ * <code>
+ *    // fire the "start" event
+ *    $responses = Efs_Event::fire('start');
+ *
+ *    // fire the "start" event passing an array of parameters
+ *    $responses = Efs_Event::fire('start', array('Pimf', 'Framework'));
+ *
+ *    // fire multiple events with the same parameters
+ *    $responses = Efs_Event::fire(array('start', 'loading'), $parameters);
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Event
+{
+  /**
+   * All registered events.
+   *
+   * @var array
+   */
+  protected static $events = array();
+
+  /**
+   * Determine if an event has any registered listeners.
+   *
+   * @param string $event
+   *
+   * @return bool
+   */
+  public static function listeners($event)
+  {
+    return isset(static::$events[$event]);
+  }
+
+  /**
+   * Register a callback for a given event.
+   *
+   * @param string $event
+   * @param mixed  $callback
+   *
+   * @return void
+   */
+  public static function listen($event, $callback)
+  {
+    static::$events[$event][] = $callback;
+  }
+
+  /**
+   * Override all callbacks for a given event with a new callback.
+   *
+   * @param string $event
+   * @param mixed  $callback
+   *
+   * @return void
+   */
+  public static function override($event, $callback)
+  {
+    static::clear($event);
+    static::listen($event, $callback);
+  }
+
+  /**
+   * Clear all event listeners for a given event.
+   *
+   * @param string $event
+   *
+   * @return void
+   */
+  public static function clear($event)
+  {
+    unset(static::$events[$event]);
+  }
+
+  /**
+   * Fire an event and return the first response.
+   *
+   * @param string $event
+   * @param \Exception[]  $parameters
+   *
+   * @return mixed
+   */
+  public static function first($event, $parameters = array())
+  {
+    $responses = static::fire($event, $parameters);
+
+    return reset($responses);
+  }
+
+  /**
+   * Fire an event and return the first response.
+   * Execution will be halted after the first valid response is found.
+   *
+   * @param string $event
+   * @param array  $parameters
+   *
+   * @return mixed
+   */
+  public static function until($event, $parameters = array())
+  {
+    return static::fire($event, $parameters, true);
+  }
+
+  /**
+   * Fire an event so that all listeners are called.
+   *
+   * @param string $events
+   * @param array        $parameters
+   * @param bool         $halt
+   *
+   * @return array|null
+   */
+  public static function fire($events, $parameters = array(), $halt = false)
+  {
+    $responses = array();
+
+    $parameters = (array)$parameters;
+
+    // If the event has listeners, iterate through them and call each listener,
+    // passing in the parameters.
+    foreach ((array)$events as $event) {
+
+      if (static::listeners($event)) {
+
+        foreach (static::$events[$event] as $callback) {
+          $response = call_user_func_array($callback, $parameters);
+
+          // If the event is set to halt,
+          // return the first response that is not null.
+          if ($halt and !is_null($response)) {
+            return $response;
+          }
+
+          $responses[] = $response;
+        }
+      }
+    }
+
+    return $halt ? null : $responses;
+  }
+}

+ 234 - 0
php-pimf/pimf-framework/core/Pimf/Logger.php

@@ -0,0 +1,234 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Logger with common logging options into a file.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Logger
+{
+  /**
+   * @var resource
+   */
+  private $handle;
+
+  /**
+   * @var resource
+   */
+  private $warnHandle;
+
+  /**
+   * @var resource
+   */
+  private $errorHandle;
+
+  /**
+   * @var string
+   */
+  private $storageDir;
+
+  /**
+   * @var bool
+   */
+  private $separator;
+
+  /**
+   * @param string $localeStorageDir Use better the local TMP dir or dir with mod 777.
+   * @param bool   $trailingSeparator
+   */
+  public function __construct($localeStorageDir, $trailingSeparator = true)
+  {
+    $this->storageDir = (string)$localeStorageDir;
+    $this->separator  = (bool)$trailingSeparator;
+  }
+
+  /**
+   * @throws \RuntimeException If something went wrong on creating the log dir and file.
+   */
+  public function init()
+  {
+    if (is_resource($this->errorHandle)
+      && is_resource($this->handle)
+      && is_resource($this->warnHandle)
+    ) {
+      return;
+    }
+
+    if (!is_dir($this->storageDir)) {
+      mkdir($this->storageDir, 0777);
+    }
+
+    if (true === $this->separator) {
+      $this->storageDir = rtrim(realpath($this->storageDir), '\\/') . DS;
+    }
+
+    $this->handle      = fopen($this->storageDir . "pimf-logs.txt", "at+");
+    $this->warnHandle  = fopen($this->storageDir . "pimf-warnings.txt", "at+");
+    $this->errorHandle = fopen($this->storageDir . "pimf-errors.txt", "at+");
+
+    if (!$this->errorHandle || !$this->handle || !$this->warnHandle) {
+      throw new \RuntimeException("failed to obtain a handle to logger file");
+    }
+  }
+
+  /**
+   * @param string $msg
+   *
+   * @return Logger
+   */
+  public function debug($msg)
+  {
+    if ($this->iniGetBool('display_errors') === true) {
+      $this->write((string)$msg, 'DEBUG');
+    }
+
+    return $this;
+  }
+
+  /**
+   * @param string $msg
+   *
+   * @return Logger
+   */
+  public function warn($msg)
+  {
+    if ($this->iniGetBool('display_errors') === true) {
+      $this->write((string)$msg, 'WARNING');
+    }
+
+    return $this;
+  }
+
+  /**
+   * @param string $msg
+   *
+   * @return Logger
+   */
+  public function error($msg)
+  {
+    $this->write((string)$msg, 'ERROR');
+
+    return $this;
+  }
+
+  /**
+   * @param string $msg
+   *
+   * @return Logger
+   */
+  public function info($msg)
+  {
+    if ($this->iniGetBool('display_errors') === true) {
+      $this->write((string)$msg, 'INFO');
+    }
+
+    return $this;
+  }
+
+  /**
+   * @param        string $msg
+   * @param string $severity
+   */
+  protected function write($msg, $severity = 'DEBUG')
+  {
+    $msg = $this->format($msg, $severity);
+
+    // if severity is WARNING then write to warning file
+    if ($severity == 'WARNING') {
+      fwrite($this->warnHandle, $msg);
+    } // if severity is ERROR then write to error file
+    else if ($severity == 'ERROR') {
+      fwrite($this->errorHandle, $msg);
+    } else {
+      fwrite($this->handle, $msg);
+    }
+  }
+
+  public function __destruct()
+  {
+    if (is_resource($this->handle)
+      && is_resource($this->warnHandle)
+      && is_resource($this->errorHandle)
+    ) {
+
+      if (fclose($this->handle) === false) {
+        // Failure to close the log file
+        $this->write("Logger failed to close the handle to the log file", 'ERROR_SEVERITY');
+      }
+
+      fclose($this->warnHandle);
+      fclose($this->errorHandle);
+    }
+  }
+
+  /**
+   * Formats the error message in representable manner.
+   *
+   * @param string $message
+   * @param string $severity
+   *
+   * @return string
+   */
+  private function format($message, $severity)
+  {
+    $registry = new Registry();
+    $remoteIP = $registry->env->getIp();
+    $script   = $registry->env->PHP_SELF;
+
+    $msg = date("m-d-Y") . " " . date("G:i:s") . " " . $remoteIP;
+
+    $IPLength       = strlen($remoteIP);
+    $numWhitespaces = 15 - $IPLength;
+
+    for ($i = 0; $i < $numWhitespaces; $i++) {
+      $msg .= " ";
+    }
+
+    $msg .= " " . $severity . ": ";
+
+    //get the file name
+    $lastSlashIndex = strrpos($script, "/");
+    $fileName       = $script;
+
+    if ($lastSlashIndex !== false) {
+      $fileName = substr($script, $lastSlashIndex + 1);
+    }
+
+    $msg .= $fileName . "\t";
+    $msg .= $severity;
+    $msg .= ": " . $message . "\r\n";
+
+    return $msg;
+  }
+
+  /**
+   * @param string $varname
+   *
+   * @return bool
+   */
+  protected function iniGetBool($varname)
+  {
+    $varvalue = ini_get($varname);
+
+    switch (strtolower($varvalue)) {
+      case 'on':
+      case 'yes':
+      case 'true':
+        return 'assert.active' !== $varname;
+      case 'stdout':
+      case 'stderr':
+        return 'display_errors' === $varname;
+      default:
+        return (bool)(int)$varvalue;
+    }
+  }
+}

+ 110 - 0
php-pimf/pimf-framework/core/Pimf/Memcached.php

@@ -0,0 +1,110 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * For use please add the following code to the end of the config.core.php file:
+ *
+ * <code>
+ *
+ * 'cache' => array(
+ *
+ *    'storage' => 'memcached',
+ *       'servers' => array(
+ *           array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100),
+ *        ),
+ *      ),
+ *  ),
+ *
+ * </code>
+ *
+ * Memcached usage:
+ *
+ * <code>
+ *    // Get the Memcache connection and get an item from the cache
+ *    $name = Memcached::connection()->get('name');
+ *
+ *    // Get the Memcache connection and place an item in the cache
+ *    Memcached::connection()->set('name', 'Robin');
+ *
+ *    // Get an item from the Memcache instance
+ *    $name = Memcached::get('name');
+ *
+ *    // Store data on the Memcache server
+ *    Memcached::set('name', 'Robin');
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @method get($key)
+ * @method put($key, $value, $expiration)
+ * @method forget($key);
+ */
+class Memcached
+{
+  /**
+   * @var \Memcached
+   */
+  protected static $connection;
+
+  /**
+   * @return \Memcached
+   */
+  public static function connection()
+  {
+    if (static::$connection === null) {
+      $conf               = Registry::get('conf');
+      static::$connection = static::connect(
+        $conf['cache']['servers']
+      );
+    }
+
+    return static::$connection;
+  }
+
+  /**
+   * Create a new Memcached connection instance.
+   *
+   * @param array $servers
+   * @param null  $memcache
+   *
+   * @return \Memcached|null
+   * @throws \RuntimeException
+   */
+  protected static function connect(array $servers, $memcache = null)
+  {
+    if (!$memcache) {
+      $memcache = new \Memcached();
+    }
+
+    foreach ($servers as $server) {
+      $memcache->addServer($server['host'], $server['port'], $server['weight']);
+    }
+
+    if ($memcache->getVersion() === false) {
+      throw new \RuntimeException('could not establish memcached connection!');
+    }
+
+    return $memcache;
+  }
+
+  /**
+   * Dynamically pass all other method calls to the Memcache instance.
+   *
+   * @param $method
+   * @param $parameters
+   *
+   * @return mixed
+   */
+  public static function __callStatic($method, $parameters)
+  {
+    return call_user_func_array(array(static::connection(), $method), $parameters);
+  }
+}

+ 55 - 0
php-pimf/pimf-framework/core/Pimf/Model/AsArray.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * Model
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Model;
+
+use Pimf\Contracts\Arrayable;
+
+/**
+ * Returns only protected and public properties of the given model-object. You have to extend it.
+ *
+ * Normally you will use ArrayObject and than method getArrayCopy() to turn Classes to Array, but
+ * with AsArray you have the opportunity to easily intercept the setting of the values at the array.
+ *
+ * Sure if you need it - otherwise please prefers using ArrayObject - is much faster!
+ *
+ * @package Model
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class AsArray implements Arrayable
+{
+  /**
+   * Returns only protected and public properties of the given model-object.
+   * For another properties output format, please override this method.
+   *
+   * @return array A list of properties.
+   */
+  public function toArray()
+  {
+    return $this->map(get_class_vars(get_class($this)));
+  }
+
+  /**
+   * Maps the properties to array with actual values.
+   * For another properties-mapping, please override this method.
+   *
+   * @param array $properties
+   *
+   * @return array
+   */
+  protected function map(array $properties)
+  {
+    $map = array();
+
+    foreach ($properties as $name => $default) {
+      $map[$name] = (true === empty($this->$name)) ? $default : $this->$name;
+    }
+
+    return $map;
+  }
+}

+ 79 - 0
php-pimf/pimf-framework/core/Pimf/Param.php

@@ -0,0 +1,79 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Param
+{
+  /**
+   * @var \ArrayObject|null
+   */
+  protected $data = null;
+
+  /**
+   * @param array $data
+   */
+  public function __construct(array $data = array())
+  {
+    $this->data = new \ArrayObject($data, \ArrayObject::STD_PROP_LIST + \ArrayObject::ARRAY_AS_PROPS);
+  }
+
+  /**
+   * @return array
+   */
+  public function getAll()
+  {
+    return (array)$this->data->getArrayCopy();
+  }
+
+  /**
+   * @param string $index
+   * @param null   $defaultValue
+   * @param bool   $filtered If you trust foreign input introduced to your PHP code - set to FALSE!
+   *
+   * @return string
+   */
+  public function get($index, $defaultValue = null, $filtered = true)
+  {
+    if ($this->data->offsetExists($index)) {
+
+      if ($filtered === true) {
+        // pretty high-level filtering here...
+        return self::filter($this->data->offsetGet($index));
+      }
+
+      return $this->data->offsetGet($index);
+    }
+
+    return $defaultValue;
+  }
+
+  /**
+   * Never ever (ever) trust foreign input introduced to your PHP code!
+   *
+   * @param array|string $rawData
+   *
+   * @return array|bool|string
+   */
+  public static function filter($rawData)
+  {
+    return is_array($rawData)
+
+      ? array_map(
+        function ($value) {
+          return \Pimf\Util\String\Clean::xss($value);
+        }, $rawData
+      )
+
+      : \Pimf\Util\String\Clean::xss($rawData);
+  }
+}

+ 55 - 0
php-pimf/pimf-framework/core/Pimf/Pdo/Connector.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * Database
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Pdo;
+
+/**
+ * Abstract class for connections and connection management.
+ *
+ * @package Database
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Connector
+{
+  /**
+   * The PDO connection options.
+   *
+   * @var array
+   */
+  protected $options = array(
+      \PDO::ATTR_CASE              => \PDO::CASE_LOWER,
+      \PDO::ATTR_ERRMODE           => \PDO::ERRMODE_EXCEPTION,
+      \PDO::ATTR_ORACLE_NULLS      => \PDO::NULL_NATURAL,
+      \PDO::ATTR_STRINGIFY_FETCHES => false,
+      \PDO::ATTR_EMULATE_PREPARES  => false,
+    );
+
+  /**
+   * Establish a PDO database connection.
+   *
+   * @param array $config
+   *
+   * @return \PDO
+   */
+  abstract public function connect(array $config);
+
+  /**
+   * Get the PDO connection options for the configuration.
+   * Developer specified options will override the default connection options.
+   *
+   * @param array $config
+   *
+   * @return array
+   */
+  protected function options($config)
+  {
+    $options = (isset($config['options'])) ? $config['options'] : array();
+
+    return $this->options + (array)$options;
+  }
+}

+ 49 - 0
php-pimf/pimf-framework/core/Pimf/Pdo/Factory.php

@@ -0,0 +1,49 @@
+<?php
+/**
+ * Database
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Pdo;
+
+/**
+ * Creates a PDO connection from the farm of connectors.
+ *
+ * @package Database
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Factory
+{
+  /**
+   * @param array $config
+   *
+   * @return \Pimf\Database
+   * @throws \RuntimeException If no driver specified or no PDO installed.
+   * @throws \UnexpectedValueException
+   */
+  public static function get(array $config)
+  {
+    if (!isset($config['driver']) or !$config['driver']) {
+      throw new \RuntimeException('no driver specified');
+    }
+
+    $driver = strtolower($config['driver']);
+
+    if (!in_array($driver, array('sqlite', 'mysql', 'sqlserver', 'postgre'), true)) {
+      throw new \UnexpectedValueException('PDO driver "' . $driver . '" not supported by PIMF');
+    }
+
+    if (!extension_loaded('pdo') or !extension_loaded('pdo_' . $driver)) {
+      throw new \RuntimeException('Please navigate to "http://php.net/manual/pdo.installation.php" '
+        . ' to find out how to install "PDO" with "pdo_' . $driver . '" on your system!');
+    }
+
+    $driver = '\Pimf\Pdo\\' . ucfirst($driver);
+
+    $pdo = new $driver();
+
+    return $pdo->connect($config);
+  }
+}

+ 45 - 0
php-pimf/pimf-framework/core/Pimf/Pdo/Mysql.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * Database
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Pdo;
+
+/**
+ * Connection management to MySQL.
+ *
+ * @package Database
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Mysql extends Connector
+{
+  /**
+   * @param array $config
+   *
+   * @return \Pimf\Database
+   */
+  public function connect(array $config)
+  {
+    $dsn = "mysql:host={$config['host']};dbname={$config['database']}";
+
+    if (isset($config['port'])) {
+      $dsn .= ";port={$config['port']}";
+    }
+
+    if (isset($config['unix_socket'])) {
+      $dsn .= ";unix_socket={$config['unix_socket']}";
+    }
+
+    $connection = new \Pimf\Database($dsn, $config['username'], $config['password'], $this->options($config));
+
+    // set to UTF-8 which should be fine for most scenarios.
+    if (isset($config['charset'])) {
+      $connection->prepare("SET NAMES '{$config['charset']}'")->execute();
+    }
+
+    return $connection;
+  }
+}

+ 53 - 0
php-pimf/pimf-framework/core/Pimf/Pdo/Postgre.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * Database
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Pdo;
+
+/**
+ * Connection management to PostgreSQL
+ *
+ * @package Database
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Postgre extends Connector
+{
+  protected $options = array(
+      \PDO::ATTR_CASE              => \PDO::CASE_LOWER,
+      \PDO::ATTR_ERRMODE           => \PDO::ERRMODE_EXCEPTION,
+      \PDO::ATTR_ORACLE_NULLS      => \PDO::NULL_NATURAL,
+      \PDO::ATTR_STRINGIFY_FETCHES => false,
+    );
+
+  /**
+   * @param array $config
+   *
+   * @return \Pimf\Database
+   */
+  public function connect(array $config)
+  {
+    $dsn = "pgsql:host={$config['host']};dbname={$config['database']}";
+
+    if (isset($config['port'])) {
+      $dsn .= ";port={$config['port']}";
+    }
+
+    $connection = new \Pimf\Database($dsn, $config['username'], $config['password'], $this->options($config));
+
+    // set to UTF-8 which should be fine for most scenarios.
+    if (isset($config['charset'])) {
+      $connection->prepare("SET NAMES '{$config['charset']}'")->execute();
+    }
+
+    // If a schema has been specified
+    if (isset($config['schema'])) {
+      $connection->prepare("SET search_path TO '{$config['schema']}'")->execute();
+    }
+
+    return $connection;
+  }
+}

+ 36 - 0
php-pimf/pimf-framework/core/Pimf/Pdo/Sqlite.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ * Database
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Pdo;
+
+/**
+ * Connection management to SQLite.
+ *
+ * @package Database
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Sqlite extends Connector
+{
+  /**
+   * @param array $config
+   *
+   * @return \Pimf\Database
+   */
+  public function connect(array $config)
+  {
+    $options = $this->options($config);
+
+    // SQLite provides supported for "in-memory" databases, which exist only for
+    // lifetime of the request. These are mainly for tests.
+    if ($config['database'] == ':memory:') {
+      return new \Pimf\Database('sqlite::memory:', null, null, $options);
+    }
+
+    return new \Pimf\Database('sqlite:' . $config['database'], null, null, $options);
+  }
+}

+ 46 - 0
php-pimf/pimf-framework/core/Pimf/Pdo/Sqlserver.php

@@ -0,0 +1,46 @@
+<?php
+/**
+ * Database
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Pdo;
+
+/**
+ * Connection management to SQL Server
+ *
+ * @package Database
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Sqlserver extends Connector
+{
+  protected $options = array(
+      \PDO::ATTR_CASE              => \PDO::CASE_LOWER,
+      \PDO::ATTR_ERRMODE           => \PDO::ERRMODE_EXCEPTION,
+      \PDO::ATTR_ORACLE_NULLS      => \PDO::NULL_NATURAL,
+      \PDO::ATTR_STRINGIFY_FETCHES => false,
+    );
+
+  /**
+   * @param array $config
+   *
+   * @return \Pimf\Database
+   */
+  public function connect(array $config)
+  {
+    // This connection string format can also be used to connect
+    // to Azure SQL Server databases.
+    $port = (isset($config['port'])) ? ',' . $config['port'] : '';
+
+    //check for dblib for mac users connecting to mssql
+    if (isset($config['dsn_type']) && !empty($config['dsn_type']) and $config['dsn_type'] == 'dblib') {
+      $dsn = "dblib:host={$config['host']}{$port};dbname={$config['database']}";
+    } else {
+      $dsn = "sqlsrv:Server={$config['host']}{$port};Database={$config['database']}";
+    }
+
+    return new \Pimf\Database($dsn, $config['username'], $config['password'], $this->options($config));
+  }
+}

+ 322 - 0
php-pimf/pimf-framework/core/Pimf/Redis.php

@@ -0,0 +1,322 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Redis usage
+ *
+ * <code>
+ *    // Get the default Redis database instance
+ *    $redis = Redis::db();
+ *
+ *    // Get a specified Redis database instance
+ *    $reids = Redis::db('redis_2');
+ *
+ *    // Execute the GET command for the "name" key
+ *    $name = Redis::db()->run('get', array('name'));
+ *
+ *    // Execute the LRANGE command for the "list" key
+ *    $list = Redis::db()->run('lrange', array(0, 5));
+ *
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @method expire($key, $seconds)
+ * @method set($key, $value)
+ * @method del($key)
+ * @method forget($key)
+ * @method get($key)
+ * @method select($database_id)
+ * @method put($session_id, $session, $lifetime);
+ */
+class Redis
+{
+  /**
+   * The address for the Redis host.
+   *
+   * @var string
+   */
+  protected $host;
+
+  /**
+   * The port on which Redis can be accessed on the host.
+   *
+   * @var int
+   */
+  protected $port;
+
+  /**
+   * The database number the connection selects on load.
+   *
+   * @var int
+   */
+  protected $database;
+
+  /**
+   * The connection to the Redis database.
+   *
+   * @var resource
+   */
+  protected $connection;
+
+  /**
+   * The active Redis database instances.
+   *
+   * @var array
+   */
+  protected static $databases = array();
+
+  /**
+   * Create a new Redis connection instance.
+   *
+   * @param string $host
+   * @param int    $port
+   * @param int    $database
+   */
+  public function __construct($host, $port, $database = 0)
+  {
+    $this->host = $host;
+    $this->port = $port;
+    $this->database = $database;
+  }
+
+  /**
+   * Get a Redis database connection instance.
+   *
+   * The given name should correspond to a Redis database in the configuration file.
+   *
+   * @param string $name
+   *
+   * @return Redis
+   * @throws \RuntimeException
+   */
+  public static function database($name = 'default')
+  {
+    if (!isset(static::$databases[$name])) {
+      $conf = Registry::get('conf');
+
+      if (!isset($conf['cache']['storage']) || $conf['cache']['storage'] != 'redis') {
+        throw new \RuntimeException("Redis database [$name] is not defined.");
+      }
+
+      static::$databases[$name]
+        = new static($conf['cache']['server']['host'], $conf['cache']['server']['port'], $conf['cache']['server']['database']);
+    }
+
+    return static::$databases[$name];
+  }
+
+  /**
+   * Execute a command against the Redis database.
+   *
+   * @param string $method
+   * @param array  $parameters
+   *
+   * @return mixed
+   */
+  public function run($method, $parameters)
+  {
+    fwrite($this->connect(), $this->command($method, (array)$parameters));
+
+    $response = trim(fgets($this->connection, 512));
+
+    return $this->parse($response);
+  }
+
+  /**
+   * Parse and return the response from the Redis database.
+   *
+   * @param string $response
+   *
+   * @return array|string
+   * @throws \RuntimeException
+   */
+  protected function parse($response)
+  {
+    switch (substr($response, 0, 1)) {
+      case '-':
+        throw new \RuntimeException('Redis error: ' . substr(trim($response), 4));
+
+      case '+':
+      case ':':
+        return $this->inline($response);
+
+      case '$':
+        return $this->bulk($response);
+
+      case '*':
+        return $this->multibulk($response);
+
+      default:
+        throw new \RuntimeException("Unknown Redis response: " . substr($response, 0, 1));
+    }
+  }
+
+  /**
+   * Establish the connection to the Redis database.
+   *
+   * @return resource
+   * @throws \RuntimeException
+   */
+  protected function connect()
+  {
+    if (!is_null($this->connection)) {
+      return $this->connection;
+    }
+
+    $this->connection = @fsockopen($this->host, $this->port, $error, $message);
+
+    if ($this->connection === false) {
+      throw new \RuntimeException("Error making Redis connection: {$error} - {$message}");
+    }
+
+    $this->select($this->database);
+
+    return $this->connection;
+  }
+
+  /**
+   * Build the Redis command based from a given method and parameters.
+   *
+   * Redis protocol states that a command should conform to the following format:
+   *
+   *     *<number of arguments> CR LF
+   *     $<number of bytes of argument 1> CR LF
+   *     <argument data> CR LF
+   *     ...
+   *     $<number of bytes of argument N> CR LF
+   *     <argument data> CR LF
+   *
+   * More information regarding the Redis protocol: http://redis.io/topics/protocol
+   *
+   * @param string $method
+   * @param $parameters
+   *
+   * @return string
+   */
+  protected function command($method, $parameters)
+  {
+    $CRLF = "\r\n";
+
+    $command = '*' . (count($parameters) + 1) . $CRLF . '$' . strlen($method) . $CRLF . strtoupper($method) . $CRLF;
+
+    foreach ($parameters as $parameter) {
+      $command .= '$' . strlen($parameter) . $CRLF . $parameter . $CRLF;
+    }
+
+    return $command;
+  }
+
+  /**
+   * Parse and handle an inline response from the Redis database.
+   *
+   * @param string $response
+   *
+   * @return string
+   */
+  protected function inline($response)
+  {
+    return substr(trim($response), 1);
+  }
+
+  /**
+   * Parse and handle a bulk response from the Redis database.
+   *
+   * @param string $head
+   *
+   * @return string
+   */
+  protected function bulk($head)
+  {
+    if ($head == '$-1') {
+      return null;
+    }
+
+    list($read, $response, $size) = array(0, '', substr($head, 1));
+
+    if ($size > 0) {
+      do {
+
+        // Calculate and read the appropriate bytes off of the Redis response.
+        $block = (($remaining = $size - $read) < 1024) ? $remaining : 1024;
+        $response .= fread($this->connection, $block);
+        $read += $block;
+
+      } while ($read < $size);
+    }
+
+    // The response ends with a trailing CRLF.
+    fread($this->connection, 2);
+
+    return $response;
+  }
+
+  /**
+   * Parse and handle a multi-bulk reply from the Redis database.
+   *
+   * @param string $head
+   *
+   * @return array
+   */
+  protected function multibulk($head)
+  {
+    if (($count = substr($head, 1)) == '-1') {
+      return null;
+    }
+
+    $response = array();
+
+    // Iterate through each bulk response in the multi-bulk and parse it out.
+    for ($i = 0; $i < $count; $i++) {
+      $response[] = $this->parse(trim(fgets($this->connection, 512)));
+    }
+
+    return $response;
+  }
+
+  /**
+   * Dynamically make calls to the Redis database.
+   *
+   * @param string $method
+   * @param array $parameters
+   *
+   * @return mixed
+   */
+  public function __call($method, $parameters)
+  {
+    return $this->run($method, $parameters);
+  }
+
+  /**
+   * Dynamically pass static method calls to the Redis instance.
+   *
+   * @param $method
+   * @param $parameters
+   *
+   * @return mixed
+   */
+  public static function __callStatic($method, $parameters)
+  {
+    return static::database()->run($method, $parameters);
+  }
+
+  /**
+   * Close the connection to the Redis database.
+   *
+   * @return void
+   */
+  public function __destruct()
+  {
+    if ($this->connection) {
+      fclose($this->connection);
+    }
+  }
+}

+ 107 - 0
php-pimf/pimf-framework/core/Pimf/Registry.php

@@ -0,0 +1,107 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * A well-known object that other objects can use to find common objects and services.
+ * Acts also as a dependency injection container.
+ *
+ * <code>
+ *  $registry = new Registry();
+ *  $registry->your_key = "123";
+ *
+ *  // or ..
+ *
+ *  Registry::set('your_key', "123")
+ *  Registry::get('your_key')
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @property EntityManager $em
+ * @property Logger        $logger
+ * @property Environment   $env
+ * @property array         $conf
+ * @property router        $router
+ */
+class Registry
+{
+  /**
+   * The temporary storage for the accumulator.
+   *
+   * @var \ArrayObject
+   */
+  protected static $battery;
+
+  /**
+   * Re-initialises the data.
+   *
+   * @return void
+   */
+  protected static function init()
+  {
+    if (!self::$battery) {
+      self::$battery = new \ArrayObject(array(), \ArrayObject::STD_PROP_LIST);
+    }
+  }
+
+  /**
+   * @param mixed $namespace The namespace or identifier.
+   * @param mixed $value     The value.
+   */
+  public function __set($namespace, $value)
+  {
+    self::set($namespace, $value);
+  }
+
+  /**
+   * @param mixed $namespace The namespace or identifier.
+   * @param mixed $value     The value.
+   *
+   * @throws \LogicException If key should be overwritten.
+   */
+  public static function set($namespace, $value)
+  {
+    self::init();
+
+    if (is_resource($value)) {
+      throw new \LogicException('storing resources in a registry is not permitted!');
+    }
+
+    self::$battery->offsetSet($namespace, $value);
+  }
+
+  /**
+   * @param mixed $namespace The namespace or identifier.
+   *
+   * @return mixed
+   */
+  public function __get($namespace)
+  {
+    return self::get($namespace);
+  }
+
+  /**
+   * @param string|integer $namespace The namespace or identifier.
+   * @param mixed          $defaultValue
+   *
+   * @return mixed|null
+   */
+  public static function get($namespace, $defaultValue = null)
+  {
+    self::init();
+
+    if (self::$battery->offsetExists($namespace)) {
+      return self::$battery->offsetGet($namespace);
+    }
+
+    return $defaultValue;
+  }
+}

+ 133 - 0
php-pimf/pimf-framework/core/Pimf/Request.php

@@ -0,0 +1,133 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Request Manager - for controlled access to the global state of the world.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Request
+{
+  /**
+   * @var Param
+   */
+  public static $postData;
+
+  /**
+   * @var Param
+   */
+  public static $getData;
+
+  /**
+   * @var Param
+   */
+  public static $cookieData;
+
+  /**
+   * @var Param
+   */
+  public static $cliData;
+
+  /**
+   * @param array $getData
+   * @param array $postData
+   * @param array $cookieData
+   * @param array $cliData
+   */
+  public function __construct(array $getData, array $postData = array(), array $cookieData = array(), array $cliData = array())
+  {
+    static::$getData    = new Param((array)self::stripSlashesIfMagicQuotes($getData));
+    static::$postData   = new Param((array)self::stripSlashesIfMagicQuotes($postData));
+    static::$cookieData = new Param($cookieData);
+    static::$cliData    = new Param((array)self::stripSlashesIfMagicQuotes($cliData));
+  }
+
+  /**
+   * HTTP GET variables.
+   *
+   * @return Param
+   */
+  public function fromGet()
+  {
+    return static::$getData;
+  }
+
+  /**
+   * CLI arguments passed to script.
+   *
+   * @return Param
+   */
+  public function fromCli()
+  {
+    return static::$cliData;
+  }
+
+  /**
+   * HTTP POST variables.
+   *
+   * @return Param
+   */
+  public function fromPost()
+  {
+    return static::$postData;
+  }
+
+  /**
+   * HTTP Cookies.
+   *
+   * @return Param
+   */
+  public function fromCookie()
+  {
+    return static::$cookieData;
+  }
+
+  /**
+   * Strip slashes from string or array
+   *
+   * @param      $rawData
+   * @param null $overrideStripSlashes
+   *
+   * @return array|string
+   */
+  public static function stripSlashesIfMagicQuotes($rawData, $overrideStripSlashes = null)
+  {
+    $hasMagicQuotes = function_exists('get_magic_quotes_gpc') ? get_magic_quotes_gpc() : false;
+
+    $strip = !$overrideStripSlashes ? $hasMagicQuotes : $overrideStripSlashes;
+
+    if ($strip) {
+      return self::stripSlashes($rawData);
+    }
+
+    return $rawData;
+  }
+
+  /**
+   * Strip slashes from string or array
+   *
+   * @param $rawData
+   *
+   * @return array|string
+   */
+  public static function stripSlashes($rawData)
+  {
+    return is_array($rawData)
+
+      ? array_map(
+        function ($value) {
+          return \Pimf\Request::stripSlashes($value);
+        }, $rawData
+      )
+
+      : stripslashes($rawData);
+  }
+}

+ 106 - 0
php-pimf/pimf-framework/core/Pimf/Resolver.php

@@ -0,0 +1,106 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Resolver\Exception as Bomb;
+use Pimf\Util\String as Str;
+
+/**
+ * Resolves the user requests to controller and action.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Resolver
+{
+  /**
+   * @var string
+   */
+  protected $controllerPath;
+
+  /**
+   * @var string
+   */
+  protected $controllerClass;
+
+  /**
+   * @var string
+   */
+  protected $repositoryPath;
+
+  /**
+   * @var Request
+   */
+  protected $request;
+
+  /**
+   * @param Request $request
+   * @param string  $repositoryPath
+   * @param string  $prefix
+   *
+   * @throws Resolver\Exception
+   */
+  public function __construct(Request $request, $repositoryPath = '/Controller', $prefix = 'Pimf\\')
+  {
+    $conf = Registry::get('conf');
+
+    $controllerName = $request->fromGet()->get('controller');
+
+    if ($conf['app']['routeable'] === true) {
+
+      $target = Registry::get('router')->find();
+
+      if ($target instanceof \Pimf\Route\Target) {
+        $controllerName = $target->getController();
+      }
+    }
+
+    if (Sapi::isCli() && $conf['environment'] == 'production') {
+      $controllerName = $request->fromCli()->get('controller');
+    }
+
+    if (!$controllerName) {
+      $controllerName = $conf['app']['default_controller'];
+    }
+
+    $this->repositoryPath  = $repositoryPath;
+    $this->request         = $request;
+    $this->controllerClass = $prefix . 'Controller\\';
+
+    $basepath   = $this->repositoryPath . '/';
+    $controller = ucfirst($controllerName);
+
+    if (Str::isEvilPath($basepath . $controller)) {
+      throw new Bomb('directory traversal attack is not funny!');
+    }
+
+    $this->controllerPath = $basepath . $controller . '.php';
+
+    if (!file_exists($this->controllerPath)) {
+      throw new Bomb('no controller found at the repository path; ' . $this->controllerPath);
+    }
+  }
+
+  /**
+   * @return \Pimf\Controller\Base
+   * @throws \Exception If no controller specified or no controller found at the repository.
+   */
+  public function process()
+  {
+    $path       = str_replace($this->repositoryPath, '', $this->controllerPath);
+    $name       = str_replace('/', $this->controllerClass, $path);
+    $controller = str_replace('.php', '', $name);
+
+    if (!class_exists($controller)) {
+      throw new Bomb('can not load class "' . $controller . '" from the repository');
+    }
+
+    return new $controller($this->request, new Response(Registry::get('env')->REQUEST_METHOD));
+  }
+}

+ 20 - 0
php-pimf/pimf-framework/core/Pimf/Resolver/Exception.php

@@ -0,0 +1,20 @@
+<?php
+/**
+ * Resolver
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Resolver;
+
+/**
+ * @package Resolver
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @codeCoverageIgnore
+ */
+class Exception extends \RuntimeException
+{
+
+}

+ 254 - 0
php-pimf/pimf-framework/core/Pimf/Response.php

@@ -0,0 +1,254 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf;
+
+use \Pimf\Util\Header, Pimf\Util\Json as UtilJson;
+
+/**
+ * Provides a simple interface around the HTTP an HTTPCache-friendly response generating.
+ * Use this class to build and the current HTTP response before it is returned to the client.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Response
+{
+  /**
+   * The request method send by the client-browser.
+   *
+   * @var string
+   */
+  protected $method = null;
+
+  /**
+   * If the response attempts to send any cached headers.
+   *
+   * @var bool
+   */
+  protected static $cached = false;
+
+  /**
+   * Type of the data will be send to the client-browser.
+   *
+   * @var string
+   */
+  protected static $typed = null;
+
+  /**
+   * @param string $requestMethod
+   *
+   * @throws \RuntimeException
+   */
+  public function __construct($requestMethod)
+  {
+    $this->method = '' . strtoupper($requestMethod);
+
+    // it is PIMF framework restriction
+    if (!in_array($this->method, array('POST', 'GET', null))) {
+      throw new \RuntimeException('unsupported request-method given');
+    }
+
+    Header::clear();
+  }
+
+  public function asJSON()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeJson();
+
+    return $this;
+  }
+
+  public function asHTML()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeTextHTML();
+
+    return $this;
+  }
+
+  public function asPDF()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypePdf();
+
+    return $this;
+  }
+
+  public function asCSV()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeCsv();
+
+    return $this;
+  }
+
+  public function asTEXT()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeTextPlain();
+
+    return $this;
+  }
+
+  public function asZIP()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeZip();
+
+    return $this;
+  }
+
+  public function asXZIP()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeXZip();
+
+    return $this;
+  }
+
+  public function asMSWord()
+  {
+    $this->preventMultipleTypes();
+    self::$typed = __FUNCTION__;
+    Header::contentTypeMSWord();
+
+    return $this;
+  }
+
+  /**
+   * Sends a download dialog to the browser.
+   *
+   * @param string $stream Can be a file-path or a string.
+   * @param string $name   Name of the stream/file should be shown.
+   */
+  public function sendStream($stream, $name)
+  {
+    Header::clear();
+    Header::sendDownloadDialog($stream, $name);
+  }
+
+  /**
+   * @param mixed $data
+   * @param bool  $exit
+   */
+  public function send($data, $exit = true)
+  {
+    $body = $data;
+
+    if (self::$typed === 'asJSON') {
+      $body = UtilJson::encode($data);
+    } elseif ($data instanceof \Pimf\View) {
+      $body = $data->render();
+    }
+
+    echo '' . $body;
+    if ($exit) {
+      exit(0);
+    }
+  }
+
+
+  /**
+   * If instead you have a page that has personalization on it
+   * (say, for example, the splash page contains local news as well),
+   * you can set a copy to be cached only by the browser.
+   *
+   * @param int $seconds Interval in seconds
+   *
+   * @return $this
+   */
+  public function cacheBrowser($seconds)
+  {
+    self::preventMultipleCaching();
+    self::$cached = true;
+    Header::cacheBrowser($seconds);
+
+    return $this;
+  }
+
+  /**
+   * If you want to try as hard as possible to keep a page from being cached anywhere.
+   *
+   * @return $this
+   */
+  public function cacheNone()
+  {
+    self::preventMultipleCaching();
+    self::$cached = true;
+    Header::cacheNone();
+
+    return $this;
+  }
+
+  /**
+   * If you want to allow a page to be cached by shared proxies for one minute.
+   *
+   * @param int $seconds Interval in seconds
+   *
+   * @return $this
+   */
+  public function cacheNoValidate($seconds = 60)
+  {
+    self::preventMultipleCaching();
+    self::$cached = true;
+    Header::cacheNoValidate($seconds);
+
+    return $this;
+  }
+
+  /**
+   * Handles setting pages that are always to be revalidated for freshness by any cache.
+   *
+   * @param int $last_modified Timestamp in seconds
+   *
+   * @return $this
+   */
+  public function exitIfNotModifiedSince($last_modified)
+  {
+    self::preventMultipleCaching();
+    self::$cached = true;
+    Header::exitIfNotModifiedSince($last_modified);
+
+    return $this;
+  }
+
+  /**
+   * @throws \RuntimeException
+   */
+  private function preventMultipleTypes()
+  {
+    if (!is_empty(self::$typed)) {
+      Header::clear();
+      throw new \RuntimeException('only one HTTP content-type can be sent!');
+    }
+  }
+
+  /**
+   * @throws \RuntimeException
+   */
+  private function preventMultipleCaching()
+  {
+    if ($this->method != 'GET') {
+      Header::clear();
+      throw new \RuntimeException('HTTP cache headers can only take effect if request was sent via GET method!');
+    }
+
+    if (self::$cached === true) {
+      Header::clear();
+      throw new \RuntimeException('only one HTTP cache-control can be sent!');
+    }
+  }
+}

+ 174 - 0
php-pimf/pimf-framework/core/Pimf/Route.php

@@ -0,0 +1,174 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Route
+ *
+ * This class is a relationship of HTTP method(s), an HTTP URI to create
+ * a Pimf application route. The Pimf application will determine
+ * the one Route object to dispatch for the current HTTP request.
+ *
+ * Each route object will have a URI pattern. This pattern must match the
+ * current HTTP request's URI for the route object to be dispatched by
+ * the Pimf application. The route pattern may contain parameters, segments
+ * prefixed with a colon (:). For example:
+ *
+ *     /controller/:action/:id
+ *
+ * When the route is dispatched, it's parameters array will be populated
+ * with the values of the corresponding HTTP request URI segments.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Route
+{
+  /**
+   * @var bool
+   */
+  private $matched = true;
+
+  /**
+   * @var array
+   */
+  private $params = array();
+
+  /**
+   * The route pattern (e.g. "/controller/:action/:id")
+   *
+   * @var string
+   */
+  private $rule;
+
+  /**
+   * Array of URL parameter names
+   *
+   * @var array
+   */
+  protected $names = array();
+
+  /**
+   * Array of URL parameter names with + at the end
+   *
+   * @var array
+   */
+  protected $namesPath = array();
+
+  /**
+   * Conditions for this route's URL parameters
+   *
+   * @var array
+   */
+  private $conditions;
+
+  /**
+   * @param string $rule
+   * @param array  $target
+   * @param array  $conditions
+   */
+  public function __construct($rule, array $target = array(), array $conditions = array())
+  {
+    $this->rule       = $rule;
+    $this->conditions = $conditions;
+
+    //convert URL params into regex patterns, construct a regex for this route, init params
+    $regex = preg_replace_callback(
+      '#:([\w]+)\+?#', array($this, 'computeUrlRegex'), str_replace(')', ')?', (string)$rule)
+    );
+
+    if (substr($rule, -1) === '/') {
+      $regex .= '?';
+    }
+
+    //cache URL params' names and values if this route matches the current HTTP request
+    $params = array();
+    if (!preg_match('#^' . $regex . '$#', self::computeUri(), $params)) {
+      $this->matched = false;
+
+      return;
+    }
+
+    foreach ($this->names as $name) {
+      if (isset($params[$name])) {
+        if (isset($this->namesPath[$name])) {
+          $this->params[$name] = explode('/', urldecode($params[$name]));
+        } else {
+          $this->params[$name] = urldecode($params[$name]);
+        }
+      }
+    }
+
+    foreach ($target as $key => $value) {
+      $this->params[$key] = $value;
+    }
+  }
+
+  /**
+   * @param array $matches
+   *
+   * @return string
+   */
+  private function computeUrlRegex(array $matches)
+  {
+    $this->names[] = $matches[1];
+
+    if (isset($this->conditions[$matches[1]])) {
+      return '(?P<' . $matches[1] . '>' . $this->conditions[$matches[1]] . ')';
+    }
+
+    if (substr($matches[0], -1) === '+') {
+
+      $this->namesPath[$matches[1]] = 1;
+
+      return '(?P<' . $matches[1] . '>.+)';
+    }
+
+    return '(?P<' . $matches[1] . '>[^/]+)';
+  }
+
+  /**
+   * @return string
+   */
+  private function computeUri()
+  {
+    $uri = Registry::get('env')->REQUEST_URI;
+    $pos = strpos($uri, '?');
+
+    if ($pos !== false) {
+      $uri = substr($uri, 0, $pos);
+    }
+
+    return $uri;
+  }
+
+  /**
+   * @return boolean
+   */
+  public function matches()
+  {
+    return $this->matched;
+  }
+
+  /**
+   * @return array
+   */
+  public function getParams()
+  {
+    return $this->params;
+  }
+
+  /**
+   * @return string
+   */
+  public function getRule()
+  {
+    return $this->rule;
+  }
+}

+ 92 - 0
php-pimf/pimf-framework/core/Pimf/Route/Target.php

@@ -0,0 +1,92 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Route;
+
+/**
+ * Target
+ *
+ * The route-target class defines which prefixes get imported
+ * and exported on the Pimf request-resolver and controller.
+ *
+ * @package Route
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Target
+{
+  /**
+   * Controller name as it appears in url
+   *
+   * @var string
+   */
+  protected $controller;
+
+  /**
+   * Controller action name as it appears in url
+   *
+   * @var string
+   */
+  protected $action = 'index';
+
+  /**
+   * List of additional params at teh URL.
+   *
+   * @var array
+   */
+  protected $params = array();
+
+  /**
+   * @param string $controller
+   */
+  public function __construct($controller)
+  {
+    $this->controller = $controller;
+  }
+
+  /**
+   * @param mixed $action
+   */
+  public function setAction($action)
+  {
+    $this->action = $action;
+  }
+
+  /**
+   * @param array $params
+   */
+  public function setParams(array $params)
+  {
+    $this->params = $params;
+  }
+
+  /**
+   * @return string
+   */
+  public function getAction()
+  {
+    return $this->action;
+  }
+
+  /**
+   * Controller-name as it appears in url.
+   *
+   * @return string
+   */
+  public function getController()
+  {
+    return $this->controller;
+  }
+
+  /**
+   * @return array
+   */
+  public function getParams()
+  {
+    return $this->params;
+  }
+}

+ 85 - 0
php-pimf/pimf-framework/core/Pimf/Router.php

@@ -0,0 +1,85 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Route\Target;
+
+/**
+ * Router
+ *
+ * This class is responsible for registering route objects, assigning names to route objects,
+ * finding routes that match the current HTTP request, and creating URLs for a named route.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Router
+{
+  /**
+   * @var Route[]
+   */
+  protected $routes = array();
+
+  public function __construct()
+  {
+    //it is a pimf-framework restriction.
+    $this->map(new Route('/:controller'))->map(new Route('/:controller/:action'))->map(new Route('/:controller/:action/:id'));
+  }
+
+  /**
+   * @param Route $route
+   *
+   * @return Router
+   */
+  public function map(Route $route)
+  {
+    $this->routes[$route->getRule()] = $route;
+
+    return $this;
+  }
+
+  /**
+   * @param Route $route
+   *
+   * @return Target
+   */
+  private function target(Route $route)
+  {
+    $params = $route->getParams();
+
+    $target = new Target($params['controller']);
+
+    unset($params['controller']);
+
+    if (isset($params['action'])) {
+      $target->setAction($params['action']);
+      unset($params['action']);
+    }
+
+    $target->setParams($params);
+
+    return $target;
+  }
+
+  /**
+   * @return bool|Target
+   */
+  public function find()
+  {
+    // check custom routes first
+    // than framework's restriction routes.
+    foreach (array_reverse($this->routes) as $route) {
+      if ($route->matches() === true) {
+        return $this->target($route);
+      }
+    }
+
+    return false;
+  }
+}

+ 77 - 0
php-pimf/pimf-framework/core/Pimf/Sapi.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+/**
+ * Handles the type of interface between web server and PHP
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Sapi
+{
+
+  /**
+   * Are we in a web environment?
+   *
+   * @return boolean
+   */
+  public static function isWeb()
+  {
+    return self::isApache() || self::isIIS() || self::isCgi();
+  }
+
+  /**
+   * Are we in a cli environment?
+   *
+   * @return boolean
+   */
+  public static function isCli()
+  {
+    return PHP_SAPI === 'cli';
+  }
+
+  /**
+   * Are we in a cgi environment?
+   *
+   * @return boolean
+   */
+  public static function isCgi()
+  {
+    return PHP_SAPI === 'cgi-fcgi' || PHP_SAPI === 'cgi';
+  }
+
+  /**
+   * Are we served through Apache[2]?
+   *
+   * @return boolean
+   */
+  public static function isApache()
+  {
+    return PHP_SAPI === 'apache2handler' || PHP_SAPI === 'apachehandler';
+  }
+
+  /**
+   * Are we served through IIS?
+   *
+   * @return boolean
+   */
+  public static function isIIS()
+  {
+    return PHP_SAPI == 'isapi';
+  }
+
+  /**
+   * @return bool
+   */
+  public static function isWindows()
+  {
+    return (boolean)(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
+  }
+}

+ 164 - 0
php-pimf/pimf-framework/core/Pimf/Session.php

@@ -0,0 +1,164 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Session\Payload;
+use Pimf\Session\Storages as Storage;
+
+/**
+ * Using the session
+ *
+ * <code>
+ *
+ *    // Retrieve the session instance and get an item
+ *    Session::instance()->get('name');
+ *
+ *    // Retrieve the session instance and place an item in the session
+ *    Session::instance()->put('name', 'Robin');
+ *
+ *    // Retrieve a value from the session
+ *    $value = Session::get('name');
+ *
+ *    // Write a value to the session storage
+ *    $value = Session::put('name', 'Robin');
+ *
+ *    // Equivalent statement using the "instance" method
+ *    $value = Session::instance()->put('name', 'Robin');
+ *
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ *
+ * @method static save()
+ */
+class Session
+{
+  /**
+   * The session singleton instance for the request.
+   *
+   * @var Payload
+   */
+  public static $instance;
+
+  /**
+   * The string name of the CSRF token stored in the session.
+   *
+   * @var string
+   */
+  const CSRF = 'csrf_token';
+
+  /**
+   * Create the session payload and load the session.
+   *
+   * @return void
+   */
+  public static function load()
+  {
+    $conf = Registry::get('conf');
+
+    static::start($conf['session']['storage']);
+
+    static::$instance->load(Cookie::get($conf['session']['cookie']));
+  }
+
+  /**
+   * Create the session payload instance for the request.
+   *
+   * @param string $storage
+   *
+   * @return void
+   */
+  public static function start($storage)
+  {
+    static::$instance = new Payload(static::factory($storage));
+  }
+
+  /**
+   * Create a new session storage instance.
+   *
+   * @param string $storage
+   *
+   * @return Storage\Storage
+   * @throws \RuntimeException
+   */
+  public static function factory($storage)
+  {
+    $conf = Registry::get('conf');
+
+    switch ($storage) {
+      case 'apc':
+        return new Storage\Apc(Cache::storage('apc'));
+
+      case 'cookie':
+        return new Storage\Cookie();
+
+      case 'file':
+        return new Storage\File($conf['session']['storage_path']);
+
+      case 'pdo':
+        return new Storage\Pdo(Pdo\Factory::get($conf['session']['database']));
+
+      case 'memcached':
+        return new Storage\Memcached(Cache::storage('memcached'));
+
+      case 'memory':
+        return new Storage\Memory();
+
+      case 'redis':
+        return new Storage\Redis(Cache::storage('redis'));
+
+      case 'dba':
+        return new Storage\Dba(Cache::storage('dba'));
+
+      default:
+        throw new \RuntimeException("Session storage [$storage] is not supported.");
+    }
+  }
+
+  /**
+   * Retrieve the active session payload instance for the request.
+   *
+   * @return Payload
+   * @throws \RuntimeException
+   */
+  public static function instance()
+  {
+    if (static::started()) {
+      return static::$instance;
+    }
+
+    throw new \RuntimeException("A storage must be set before using the session.");
+  }
+
+  /**
+   * Determine if session handling has been started for the request.
+   *
+   * @return bool
+   */
+  public static function started()
+  {
+    return (static::$instance !== null);
+  }
+
+  /**
+   * Magic Method for calling the methods on the session singleton instance.
+   *
+   * @param $method
+   * @param $parameters
+   *
+   * @return mixed
+   */
+  public static function __callStatic($method, $parameters)
+  {
+    return call_user_func_array(
+      array(static::instance(), $method), $parameters
+    );
+  }
+}

+ 337 - 0
php-pimf/pimf-framework/core/Pimf/Session/Payload.php

@@ -0,0 +1,337 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session;
+
+use Pimf\Registry;
+use Pimf\Session;
+use Pimf\Util\String;
+use Pimf\Cookie;
+
+/**
+ * Using the session payload
+ *
+ * <code>
+ *
+ *    // Get an item from the session
+ *    $name = Session::get('name');
+ *
+ *    // Return a default value if the item doesn't exist
+ *    $name = Session::get('name', 'Robin');
+ *
+ *    // Write an item to the session payload
+ *    Session::put('name', 'Robin');
+ *
+ *    // Write an item to the session payload's flash data
+ *    Session::flash('name', 'Robin');
+ *
+ *    // Keep the "name" item from expiring from the flash data
+ *    Session::keep('name');
+ *
+ *    // Keep the "name" and "email" items from expiring from the flash data
+ *    Session::keep(array('name', 'email'));
+ *
+ * </code>
+ *
+ * @package Session
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Payload
+{
+  /**
+   * The session array that is stored by the storage.
+   *
+   * @var array
+   */
+  public $session;
+
+  /**
+   * The session storage used to retrieve and store the session payload.
+   *
+   * @var \Pimf\Session\Storages\Storage
+   */
+  public $storage;
+
+  /**
+   * Indicates if the session already exists in storage.
+   *
+   * @var bool
+   */
+  public $exists = true;
+
+  /**
+   * @param Storages\Storage $storage
+   */
+  public function __construct(\Pimf\Session\Storages\Storage $storage)
+  {
+    $this->storage = $storage;
+  }
+
+  /**
+   * Load the session for the current request.
+   *
+   * @param null|string $key
+   */
+  public function load($key)
+  {
+    if ($key !== null) {
+      $this->session = $this->storage->load($key);
+    }
+
+    // If the session doesn't exist or is invalid.
+    if (is_null($this->session) or static::expired($this->session)) {
+      $this->exists  = false;
+      $this->session = $this->storage->fresh();
+    }
+
+    // A CSRF token is stored in every session to protect
+    // the application from cross-site request
+    if (!$this->has(Session::CSRF)) {
+      $this->put(Session::CSRF, String::random(40));
+    }
+  }
+
+  /**
+   * Determine if the session payload instance is valid.
+   *
+   * @param array $session
+   *
+   * @return bool
+   */
+  protected static function expired($session)
+  {
+    $conf = Registry::get('conf');
+
+    if (array_key_exists('last_activity', $session)) {
+      return (time() - $session['last_activity']) > ($conf['session']['lifetime'] * 60);
+    }
+
+    return false;
+  }
+
+  /**
+   * Determine if the session or flash data contains an item.
+   *
+   * @param string $key
+   *
+   * @return bool
+   */
+  public function has($key)
+  {
+    return ($this->get($key) !== null);
+  }
+
+  /**
+   * Get an item from the session.
+   *
+   * @param string $key
+   * @param null   $default
+   *
+   * @return mixed|null
+   */
+  public function get($key, $default = null)
+  {
+    $session = $this->session['data'];
+
+    // check first for the item in the general session data.
+    if (null !== ($value = $this->isIn($key, $session))) {
+      return $value;
+    }
+
+    // or find it in the new session data.
+    if (null !== ($value = $this->isIn($key, $session[':new:']))) {
+      return $value;
+    }
+
+    // or finally return the default value.
+    if (null !== ($value = $this->isIn($key, $session[':old:']))) {
+      return $value;
+    }
+
+    return $default;
+  }
+
+  /**
+   * Checks if key is in session.
+   *
+   * @param string $key
+   * @param array  $session
+   *
+   * @return mixed|null
+   */
+  protected function isIn($key, array $session)
+  {
+    if (array_key_exists($key, $session) and $session[$key] !== null) {
+      return $session[$key];
+    }
+
+    return null;
+  }
+
+  /**
+   * Write an item to the session.
+   *
+   * @param $key
+   * @param string $value
+   */
+  public function put($key, $value)
+  {
+    $this->session['data'][$key] = $value;
+  }
+
+  /**
+   * Write an item to the session flash data.
+   *
+   * @param $key
+   * @param $value
+   */
+  public function flash($key, $value)
+  {
+    $this->session['data'][':new:'][$key] = $value;
+  }
+
+  /**
+   * Keep all of the session flash data from expiring after the request.
+   *
+   * @return void
+   */
+  public function reflash()
+  {
+    $this->session['data'][':new:'] = array_merge(
+      $this->session['data'][':new:'], $this->session['data'][':old:']
+    );
+  }
+
+  /**
+   * Keep a session flash item from expiring at the end of the request.
+   *
+   * @param $keys
+   */
+  public function keep($keys)
+  {
+    foreach ((array)$keys as $key) {
+      $this->flash($key, $this->get($key));
+    }
+  }
+
+  /**
+   * Remove an item from the session data.
+   *
+   * @param $key
+   */
+  public function forget($key)
+  {
+    unset($this->session['data'][$key]);
+  }
+
+  /**
+   * Remove all of the items from the session (CSRF token will not be removed).
+   */
+  public function flush()
+  {
+    $this->session['data'] = array(Session::CSRF => $this->token(), ':new:' => array(), ':old:' => array());
+  }
+
+  /**
+   * Assign a new, random ID to the session.
+   */
+  public function regenerate()
+  {
+    $this->session['id'] = $this->storage->id();
+    $this->exists        = false;
+  }
+
+  /**
+   * Get the CSRF token that is stored in the session data.
+   *
+   * @return string
+   */
+  public function token()
+  {
+    return $this->get(Session::CSRF);
+  }
+
+  /**
+   * Get the last activity for the session.
+   *
+   * @return int
+   */
+  public function activity()
+  {
+    return $this->session['last_activity'];
+  }
+
+  /**
+   * Store the session payload in storage.
+   * This method will be called automatically at the end of the request.
+   *
+   * @return void
+   */
+  public function save()
+  {
+    $this->session['last_activity'] = time();
+
+    // age it that should expire at the end of the user's next request.
+    $this->age();
+
+    $conf        = Registry::get('conf');
+    $sessionConf = $conf['session'];
+
+    // save data into the specialized storage.
+    $this->storage->save($this->session, $sessionConf, $this->exists);
+
+    // determine the owner of the session
+    // on the user's subsequent requests to the application.
+    $this->cookie($sessionConf);
+
+    // calculate and run garbage collection cleaning.
+    $cleaning = $sessionConf['garbage_collection'];
+
+    if (mt_rand(1, $cleaning[1]) <= $cleaning[0]) {
+      $this->clean();
+    }
+  }
+
+  /**
+   * Clean up expired sessions.
+   *
+   * @return void
+   */
+  public function clean()
+  {
+    if ($this->storage instanceof \Pimf\Contracts\Cleanable) {
+      $conf = Registry::get('conf');
+      $this->storage->clean(time() - ($conf['session']['lifetime'] * 60));
+    }
+  }
+
+  /**
+   * Age the session flash data.
+   *
+   * @return void
+   */
+  protected function age()
+  {
+    $this->session['data'][':old:'] = $this->session['data'][':new:'];
+    $this->session['data'][':new:'] = array();
+  }
+
+  /**
+   * Send the session ID cookie to the browser.
+   *
+   * @param  array $config
+   *
+   * @return void
+   */
+  protected function cookie($config)
+  {
+    $minutes = (!$config['expire_on_close']) ? $config['lifetime'] : 0;
+
+    Cookie::put($config['cookie'], $this->session['id'], $minutes, $config['path'], $config['domain'], $config['secure']);
+  }
+}

+ 61 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Apc.php

@@ -0,0 +1,61 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Apc extends Storage
+{
+
+  /**
+   * @var \Pimf\Cache\Storages\Apc
+   */
+  private $apc;
+
+  /**
+   * @param \Pimf\Cache\Storages\Apc $apc
+   */
+  public function __construct(\Pimf\Cache\Storages\Apc $apc)
+  {
+    $this->apc = $apc;
+  }
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return array|mixed|null
+   */
+  public function load($key)
+  {
+    return $this->apc->get($key);
+  }
+
+  /**
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    $this->apc->put($session['id'], $session, $config['lifetime']);
+  }
+
+  /**
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    $this->apc->forget($key);
+  }
+
+}

+ 59 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Cookie.php

@@ -0,0 +1,59 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+use Pimf\Cookie as Crumb;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Cookie extends Storage
+{
+  /**
+   * The name of the cookie used to store the session payload.
+   *
+   * @var string
+   */
+  const PAYLOAD = 'session_payload';
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param  string $key
+   *
+   * @return array
+   */
+  public function load($key)
+  {
+    if (Crumb::has(static::PAYLOAD)) {
+      return unserialize(base64_decode(Crumb::get(static::PAYLOAD)));
+    }
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    Crumb::put(static::PAYLOAD, base64_encode(serialize($session)), $config['lifetime'], $config['path'], $config['domain']);
+  }
+
+  /**
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    Crumb::forget(static::PAYLOAD);
+  }
+}

+ 79 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Dba.php

@@ -0,0 +1,79 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+use Pimf\Contracts\Cleanable;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Dba extends Storage implements Cleanable
+{
+  /**
+   * @var \Pimf\Cache\Storages\Dba
+   */
+  private $dba;
+
+  /**
+   * @param \Pimf\Cache\Storages\Dba $dba
+   */
+  public function __construct(\Pimf\Cache\Storages\Dba $dba)
+  {
+    $this->dba = $dba;
+  }
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return array|mixed
+   */
+  public function load($key)
+  {
+    return $this->dba->get($key);
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    $this->dba->put($session['id'], $session, $config['lifetime']);
+  }
+
+  /**
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    $this->dba->forget($key);
+  }
+
+  /**
+   * Delete all expired sessions from persistent storage.
+   *
+   * @param int $expiration
+   *
+   * @return mixed|void
+   */
+  public function clean($expiration)
+  {
+    $this->dba->clean();
+
+    if (filemtime($this->dba->getFile()) < $expiration) {
+      $this->dba->flush();
+    }
+  }
+}

+ 91 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/File.php

@@ -0,0 +1,91 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+use Pimf\Contracts\Cleanable;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class File extends Storage implements Cleanable
+{
+  /**
+   * The path to which the session files should be written.
+   *
+   * @var string
+   */
+  private $path;
+
+  /**
+   * @param string $path
+   */
+  public function __construct($path)
+  {
+    $this->path = (string)$path;
+  }
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return array|mixed
+   */
+  public function load($key)
+  {
+    if (file_exists($path = $this->path . $key)) {
+      return unserialize(file_get_contents($path));
+    }
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    file_put_contents($this->path . $session['id'], serialize($session), LOCK_EX);
+  }
+
+  /**
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    if (file_exists($this->path . $key)) {
+      @unlink($this->path . $key);
+    }
+  }
+
+  /**
+   * Delete all expired sessions from persistent storage.
+   *
+   * @param int $expiration
+   *
+   * @return mixed|void
+   */
+  public function clean($expiration)
+  {
+    $files = glob($this->path . '*');
+
+    if ($files === false) {
+      return;
+    }
+
+    foreach ($files as $file) {
+      if (filetype($file) == 'file' && filemtime($file) < $expiration) {
+        @unlink($file);
+      }
+    }
+  }
+}

+ 63 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Memcached.php

@@ -0,0 +1,63 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Memcached extends Storage
+{
+  /**
+   * The Memcache cache storage instance.
+   *
+   * @var \Pimf\Cache\Storages\Memcached
+   */
+  private $memcached;
+
+  /**
+   * @param \Pimf\Cache\Storages\Memcached $memcached
+   */
+  public function __construct(\Pimf\Cache\Storages\Memcached $memcached)
+  {
+    $this->memcached = $memcached;
+  }
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return array|mixed|null
+   */
+  public function load($key)
+  {
+    return $this->memcached->get($key);
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    $this->memcached->put($session['id'], $session, $config['lifetime']);
+  }
+
+  /**
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    $this->memcached->forget($key);
+  }
+}

+ 57 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Memory.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Memory extends Storage
+{
+  /**
+   * The session payload that will be returned by the storage.
+   *
+   * @var array
+   */
+  public $session;
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return array
+   */
+  public function load($key)
+  {
+    return $this->session;
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    //...
+  }
+
+  /**
+   * Delete a session from storage by a given ID.
+   *
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    //...
+  }
+}

+ 116 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Pdo.php

@@ -0,0 +1,116 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+use Pimf\Contracts\Cleanable;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Pdo extends Storage implements Cleanable
+{
+  /**
+   * @var \Pimf\Database
+   */
+  protected $pdo;
+
+  /**
+   * @param \Pimf\Database $pdo
+   */
+  public function __construct(\Pimf\Database $pdo)
+  {
+    $this->pdo = $pdo;
+  }
+
+  /**
+   * Load a session from storage by a given ID.
+   * If no session is found for the ID, null will be returned.
+   *
+   * @param string $key
+   *
+   * @return array|null
+   */
+  public function load($key)
+  {
+    try {
+      $sth = $this->pdo->prepare(
+        'SELECT * FROM sessions WHERE id = :id'
+      );
+
+      $sth->bindValue(':id', $key, \PDO::PARAM_INT);
+      $sth->execute();
+
+      $session = $sth->fetchObject();
+
+      if ($session instanceof \stdClass) {
+        return array('id' => $session->id, 'last_activity' => $session->last_activity, 'data' => unserialize($session->data));
+      }
+    } catch (\PDOException $pdoe) {
+      return null;
+    }
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    if ($exists) {
+      $sth = $this->pdo->prepare(
+        "INSERT INTO sessions (id, last_activity, data) VALUES (:id, :last_activity, :data)"
+      );
+    } else {
+      $sth = $this->pdo->prepare(
+        "UPDATE sessions SET last_activity = :last_activity, data = :data WHERE id = :id"
+      );
+    }
+
+    $sth->bindValue(':id', $session['id'], \PDO::PARAM_INT);
+    $sth->bindValue(':last_activity', $session['last_activity']);
+    $sth->bindValue(':data', serialize($session['data']));
+    $sth->execute();
+  }
+
+  /**
+   * Delete a session from storage by a given ID.
+   *
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    $sth = $this->pdo->prepare(
+      "DELETE FROM sessions WHERE id = :id"
+    );
+
+    $sth->bindValue(':id', $key, \PDO::PARAM_INT);
+    $sth->execute();
+  }
+
+  /**
+   * Delete all expired sessions from persistent storage.
+   *
+   * @param int $expiration
+   *
+   * @return mixed|void
+   */
+  public function clean($expiration)
+  {
+    $sth = $this->pdo->prepare(
+      "DELETE FROM sessions WHERE last_activity < :expiration"
+    );
+
+    $sth->bindValue(':expiration', $expiration);
+    $sth->execute();
+  }
+}

+ 63 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Redis.php

@@ -0,0 +1,63 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Redis extends Storage
+{
+  /**
+   * The Redis cache storage instance.
+   *
+   * @var \Pimf\Cache\Storages\Redis
+   */
+  protected $redis;
+
+  /**
+   * @param \Pimf\Cache\Storages\Redis $redis
+   */
+  public function __construct(\Pimf\Cache\Storages\Redis $redis)
+  {
+    $this->redis = $redis;
+  }
+
+  /**
+   * Load a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return array|mixed|null
+   */
+  public function load($key)
+  {
+    return $this->redis->get($key);
+  }
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   */
+  public function save($session, $config, $exists)
+  {
+    $this->redis->put($session['id'], $session, $config['lifetime']);
+  }
+
+  /**
+   * @param string $key
+   */
+  public function delete($key)
+  {
+    $this->redis->forget($key);
+  }
+}

+ 78 - 0
php-pimf/pimf-framework/core/Pimf/Session/Storages/Storage.php

@@ -0,0 +1,78 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Session\Storages;
+
+use Pimf\Util\String;
+
+/**
+ * @package Session_Storages
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Storage
+{
+  /**
+   * Load a session from storage by a given ID.
+   * If no session is found for the id, null will be returned.
+   *
+   * @param string $key
+   *
+   * @return array|null
+   */
+  abstract public function load($key);
+
+  /**
+   * Save a given session to storage.
+   *
+   * @param array $session
+   * @param array $config
+   * @param bool  $exists
+   *
+   * @return void
+   */
+  abstract public function save($session, $config, $exists);
+
+  /**
+   * Delete a session from storage by a given ID.
+   *
+   * @param string $key
+   *
+   * @return void
+   */
+  abstract public function delete($key);
+
+  /**
+   * Create a fresh session array with a unique ID.
+   *
+   * @return array
+   */
+  public function fresh()
+  {
+    return array('id' => $this->id(), 'data' => array(':new:' => array(), ':old:' => array(),));
+  }
+
+  /**
+   * Get a new session ID that isn't assigned to any current session.
+   *
+   * @return string
+   */
+  public function id()
+  {
+    // just return any string since the Cookie storage has no idea.
+    if ($this instanceof \Pimf\Session\Storages\Cookie) {
+      return String::random(40);
+    }
+
+    // we'll find an random ID here.
+    do {
+      $session = $this->load($key = String::random(40));
+    } while ($session !== null);
+
+    return $key;
+  }
+}

+ 77 - 0
php-pimf/pimf-framework/core/Pimf/Uri.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf;
+
+use Pimf\Util\String as Str;
+
+/**
+ * URI
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Uri
+{
+
+  /**
+   * The URI for the current request.
+   *
+   * @var string
+   */
+  public static $uri;
+
+  /**
+   * The URI segments for the current request.
+   *
+   * @var array
+   */
+  public static $segments = array();
+
+  /**
+   * Get the full URI including the query string.
+   *
+   * @return string
+   */
+  public static function full()
+  {
+    return Registry::get('env')->REQUEST_URI;
+  }
+
+  /**
+   * Get the URI for the current request.
+   *
+   * @return string
+   */
+  public static function current()
+  {
+    if (!is_null(static::$uri)) {
+      return static::$uri;
+    }
+
+    //Format a given URI.
+    $uri = trim(Registry::get('env')->PATH_INFO, '/') ? : '/';
+
+    //Set the URI segments for the request.
+    $segments         = explode('/', trim($uri, '/'));
+    static::$segments = array_diff($segments, array(''));
+
+    return static::$uri = $uri;
+  }
+
+  /**
+   * Determine if the current URI matches a given pattern.
+   *
+   * @param  string $pattern
+   *
+   * @return bool
+   */
+  public static function is($pattern)
+  {
+    return Str::is($pattern, static::current());
+  }
+}

+ 232 - 0
php-pimf/pimf-framework/core/Pimf/Url.php

@@ -0,0 +1,232 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf;
+
+use Pimf\Util\String as Str;
+
+/**
+ * URL
+ *
+ * <code>
+ *    // create a URL to a location within the application
+ *    $url = Url::to('user/profile');
+ *
+ *    // create a HTTPS URL to a location within the application
+ *    $url = Url::to('user/profile', true);
+ * </code>
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Url
+{
+  /**
+   * The cached base URL.
+   *
+   * @var string
+   */
+  public static $base;
+
+  /**
+   * Get the full URI including the query string.
+   *
+   * @return string
+   */
+  public static function full()
+  {
+    return static::to(Uri::full());
+  }
+
+  /**
+   * Get the full URL for the current request.
+   *
+   * @return string
+   */
+  public static function current()
+  {
+    return static::to(Uri::current(), null, false);
+  }
+
+  /**
+   * Get the URL for the application root.
+   *
+   * @param null|bool $https
+   *
+   * @return string
+   */
+  public static function home($https = null)
+  {
+    return static::to('/', $https);
+  }
+
+  /**
+   * Get the base URL of the application.
+   *
+   * @return string
+   */
+  public static function base()
+  {
+    if (isset(static::$base)) {
+      return static::$base;
+    }
+
+    $conf = Registry::get('conf');
+    $url  = $conf['app']['url'];
+
+    if ($url !== '') {
+      $base = $url;
+    } else {
+      $base = Registry::get('env')->getUrl();
+    }
+
+    return static::$base = $base;
+  }
+
+  /**
+   * Generate an application URL.
+   *
+   * @param string    $url
+   * @param null|bool $https
+   * @param bool      $asset
+   *
+   * @return string
+   */
+  public static function to($url = '', $https = null, $asset = false)
+  {
+    $url = trim($url, '/');
+
+    if (static::valid($url)) {
+      return $url;
+    }
+
+    $root = self::format($https, $asset);
+
+    return rtrim($root, '/') . '/' . ltrim($url, '/');
+  }
+
+  /**
+   * Computes the URl method
+   *
+   * @param null|bool $https
+   * @param bool      $asset
+   *
+   * @return string
+   */
+  private static function format($https = null, $asset = false)
+  {
+    $root = static::base();
+    $conf = Registry::get('conf');
+
+    if (!$asset) {
+      $root .= '/' . $conf['app']['index'];
+    }
+
+    // Unless $https is specified we set https for all secure links.
+    if (is_null($https)) {
+      $https = Registry::get('env')->isHttps();
+    }
+
+    // disable SSL on all framework generated links to make it more
+    // convenient to work with the site while developing locally.
+    if ($https and $conf['ssl']) {
+      return preg_replace('~http://~', 'https://', $root, 1);
+    }
+
+    return preg_replace('~https://~', 'http://', $root, 1);
+  }
+
+  /**
+   * Generate an application URL with HTTPS.
+   *
+   * @param string $url
+   *
+   * @return string
+   */
+  public static function as_https($url = '')
+  {
+    return static::to($url, true);
+  }
+
+  /**
+   * Generate an application URL to an asset.
+   *
+   * @param  string $url
+   * @param  bool   $https
+   *
+   * @return string
+   */
+  public static function to_asset($url, $https = null)
+  {
+    if (static::valid($url) or static::valid('http:' . $url)) {
+      return $url;
+    }
+
+    $conf = Registry::get('conf');
+    $root = ($conf['app']['asset_url'] != '') ? $conf['app']['asset_url'] : false;
+
+    // shoot us through a different server or third-party content delivery network.
+    if ($root) {
+      return rtrim($root, '/') . '/' . ltrim($url, '/');
+    }
+
+    $url = static::to($url, $https, true);
+
+    // we do not need to come through the front controller.
+    if ($conf['app']['index'] !== '') {
+      $url = str_replace($conf['app']['index'] . '/', '', $url);
+    }
+
+    return $url;
+  }
+
+  /**
+   * Determine if the given URL is valid.
+   *
+   * @param  string $url
+   *
+   * @return bool
+   */
+  public static function valid($url)
+  {
+    if (Str::startsWith($url, '//')) {
+      return true;
+    }
+
+    return filter_var($url, FILTER_VALIDATE_URL) !== false;
+  }
+
+  /**
+   * Get cleaner URLs or old-fashioned » RFC 3986 URL-query string.
+   *
+   * @param string $route controller/action
+   * @param array  $params
+   * @param null   $https
+   * @param bool   $asset
+   *
+   * @return string
+   */
+  public static function compute($route = '', array $params = array(), $https = null, $asset = false)
+  {
+    // if your application should work with RFC 3986 URL-query strings
+    $conf = Registry::get('conf');
+    if ($conf['app']['routeable'] === false) {
+      list($controller, $action) = explode('/', $route);
+      $params = array_merge(compact('controller', 'action'), $params);
+
+      return Str::ensureTrailing('/', self::format($https, $asset)) . '?' . http_build_query($params, null, '&');
+    }
+
+    // otherwise PIMF will serve you cleaner URLs
+    $slug = implode('/', $params);
+    if ($slug != '') {
+      $slug = '/' . $slug;
+    }
+
+    return self::to($route, $https, $asset) . $slug;
+  }
+}

+ 75 - 0
php-pimf/pimf-framework/core/Pimf/Util/Dom.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * A powerful tool for HTML or XML document manipulation and extraction of data.
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Dom extends \DOMDocument
+{
+  /**
+   * Retries a list of attributes-value which can live inside a HTML/XML-Element
+   *
+   * @param string $tag       A HTML/XML tag-name representation for the HTML/XML-Element
+   * @param string $attribute A attribute inside of the HTML/XML-Element
+   *
+   * @return array
+   */
+  public function fetchValues($tag, $attribute)
+  {
+    $values = array();
+
+    // loop through each tag in the dom and add it to the array
+    foreach ($this->getElementsByTagName($tag) as $tag) {
+      /* @var $tag \DOMElement */
+      $values[] = $tag->getAttribute($attribute);
+    }
+
+    return $values;
+  }
+
+  /**
+   * Grab all links in a page
+   *
+   * @return array
+   */
+  public function getURLs()
+  {
+    return $this->fetchValues('a', 'href');
+  }
+
+  /**
+   * Grab all URLs of an image
+   *
+   * @return array
+   */
+  public function getImageURLs()
+  {
+    return $this->fetchValues('img', 'src');
+  }
+
+  /**
+   * Grab all URLs of an external script file like JS
+   */
+  public function getScriptURLs()
+  {
+    return $this->fetchValues('script', 'src');
+  }
+
+  /**
+   * Grab all URLs location of the linked document like CSS
+   */
+  public function getCssURLs()
+  {
+    return $this->fetchValues('link', 'href');
+  }
+}

+ 105 - 0
php-pimf/pimf-framework/core/Pimf/Util/File.php

@@ -0,0 +1,105 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c) 2010-2013 Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * Represents a file in the file system and uses SplFileInfo a high-level object oriented interface to
+ * information for an individual file.
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class File extends \SplFileInfo
+{
+  /**
+   * Constructs a new file from the given path.
+   *
+   * @param string  $path  The path to the file
+   * @param boolean $check Whether to check the path or not
+   *
+   * @throws \OutOfRangeException If the given path is not a file
+   */
+  public function __construct($path, $check = true)
+  {
+    if ($check && !is_file($path)) {
+      throw new \OutOfRangeException("path $path is not a file");
+    }
+
+    parent::__construct($path);
+  }
+
+  /**
+   * Returns the extension of the file.
+   * SplFileInfo::getExtension() is not available before PHP 5.3.6
+   *
+   * @return string
+   */
+  public function getExtension()
+  {
+    return pathinfo($this->getBasename(), PATHINFO_EXTENSION);
+  }
+
+  /**
+   * Moves the file to a new location.
+   *
+   * @param string $dir  The destination folder
+   * @param string $name The new file name
+   *
+   * @return File A File object representing the new file
+   * @throws \RuntimeException if the target file could not be created
+   */
+  public function move($dir, $name = null)
+  {
+    $target = $this->getTargetFile($dir, $name);
+
+    if (!@rename($this->getPathname(), $target)) {
+      $error = error_get_last();
+      throw new \RuntimeException("Could not move the file {$this->getPathname()} to $target ({$error['message']})");
+    }
+
+    @chmod($target, 0666 & ~umask());
+
+    return $target;
+  }
+
+  /**
+   * @param string      $dir The destination folder
+   * @param null|string $name
+   *
+   * @return File
+   * @throws \RuntimeException
+   */
+  protected function getTargetFile($dir, $name = null)
+  {
+    if (!is_dir($dir) || false === @mkdir($dir, 0777, true)) {
+        throw new \RuntimeException("The destination folder $dir");
+    }
+
+    if (!is_writable($dir)) {
+      throw new \RuntimeException("Unable to write in the $dir directory");
+    }
+
+    return new self($dir . DS . (null === $name ? $this->getBasename() : $this->getName($name)), false);
+  }
+
+  /**
+   * Returns locale independent base name of the given path.
+   *
+   * @param string $name The new file name
+   *
+   * @return string
+   */
+  protected function getName($name)
+  {
+    $originalName = str_replace('\\', '/', $name);
+    $pos          = strrpos($originalName, '/');
+
+    return (false === $pos) ? $originalName : substr($originalName, $pos + 1);
+  }
+}

+ 204 - 0
php-pimf/pimf-framework/core/Pimf/Util/Header.php

@@ -0,0 +1,204 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+use Pimf\Registry;
+use Pimf\Sapi;
+
+/**
+ * Manages a raw HTTP header sending.
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Header extends Header\ContentType
+{
+  /**
+   * Removes previously set headers.
+   */
+  public static function clear()
+  {
+    if (!headers_sent() && error_get_last() == null) {
+      header_remove();
+    }
+  }
+
+  /**
+   * @param string  $url
+   * @param boolean $exit
+   */
+  public static function toLocation($url, $exit = true)
+  {
+    header('Location: ' . $url);
+    if ($exit) {
+      exit(1);
+    }
+  }
+
+  /**
+   * @param int     $code
+   * @param string  $status
+   * @param boolean $exit
+   */
+  protected static function view($code, $status, $exit = true)
+  {
+    if (Sapi::isCli()) {
+      echo $status . PHP_EOL;
+      if ($exit) {
+        exit;
+      }
+    }
+
+    self::send($code, $status);
+
+    $conf    = Registry::get('conf');
+    $appTpl  = str_replace('/', DS, BASE_PATH . 'app/' . $conf['app']['name'] . '/_error/' . $code . '.php');
+    $coreTpl = str_replace('/', DS, BASE_PATH . 'core/Pimf/_error/' . $code . '.php');
+
+    if (file_exists($appTpl) && is_readable($appTpl)) {
+      include $appTpl;
+      if ($exit) {
+        exit(1);
+      }
+    }
+
+    include $coreTpl;
+    if ($exit) {
+      exit(1);
+    }
+  }
+
+  /**
+   * @param string  $msg
+   * @param boolean $exit
+   */
+  public static function sendInternalServerError($msg = '', $exit = true)
+  {
+    self::view(500, $msg, $exit);
+  }
+
+  /**
+   * @param string  $msg
+   * @param boolean $exit
+   */
+  public static function sendNotFound($msg = '', $exit = true)
+  {
+    self::view(404, $msg, $exit);
+  }
+
+  /**
+   * Handles setting pages that are always to be revalidated for freshness by any cache.
+   *
+   * @param int $last_modified Timestamp in seconds
+   */
+  public static function exitIfNotModifiedSince($last_modified)
+  {
+    if (self::isModified($last_modified)) {
+      self::sendNotModified();
+      exit(0);
+    }
+
+    $last_modified = gmdate('D, d M Y H:i:s', $last_modified) . ' GMT';
+    header("Cache-Control: must-revalidate");
+    header("Last Modified: $last_modified");
+  }
+
+  /**
+   * Actual HTTP caching validation.
+   *
+   * @param int    $mtime In seconds
+   * @param string $etag
+   *
+   * @return bool
+   */
+  public static function isModified($mtime, $etag = '')
+  {
+    $env = Registry::get('env');
+
+    $modified_since = strtotime(preg_replace('/;.*$/', '', $env->HTTP_IF_MODIFIED_SINCE));
+
+    return !($modified_since >= $mtime || $env->HTTP_IF_NONE_MATCH == $etag);
+  }
+
+  /**
+   * If you want to allow a page to be cached by shared proxies for one minute.
+   *
+   * @param int $seconds Interval in seconds
+   */
+  public static function cacheNoValidate($seconds = 60)
+  {
+    $now    = time();
+    $lmtime = gmdate('D, d M Y H:i:s', $now) . ' GMT';
+    $extime = gmdate('D, d M Y H:i:s', $now + $seconds) . 'GMT';
+    // backwards compatibility for HTTP/1.0 clients
+    header("Last Modified: $lmtime");
+    header("Expires: $extime");
+    // HTTP/1.1 support
+    header("Cache-Control: public,max-age=$seconds");
+  }
+
+  /**
+   * If instead you have a page that has personalization on it
+   * (say, for example, the splash page contains local news as well),
+   * you can set a copy to be cached only by the browser.
+   *
+   * @param int $seconds Interval in seconds
+   */
+  public static function cacheBrowser($seconds = 60)
+  {
+    $now    = time();
+    $lmtime = gmdate('D, d M Y H:i:s', $now) . ' GMT';
+    $extime = gmdate('D, d M Y H:i:s', $now + $seconds) . ' GMT';
+    // backwards compatibility for HTTP/1.0 clients
+    header("Last Modified: $lmtime");
+    header("Expires: $extime");
+    // HTTP/1.1 support
+    header("Cache-Control: private,max-age=$seconds,s-maxage=0");
+  }
+
+
+  /**
+   * If you want to try as hard as possible to keep a page from being cached anywhere.
+   */
+  public static function cacheNone()
+  {
+    // backwards compatibility for HTTP/1.0 clients
+    header("Expires: 0");
+    header("Pragma: no-cache");
+    // HTTP/1.1 support
+    header("Cache-Control: no-cache,no-store,max-age=0,s-maxage=0,must-revalidate");
+  }
+
+  /**
+   * Sends file as download-header through any firewall to the browsers like >=IE6 >=FF3.6, Safari, Chrome, Opera.
+   *
+   * @link http://reeg.junetz.de/DSP/node16.html
+   * @link http://www.php.net/manual/de/function.header.php#88038
+   *
+   * @param string $fileOrString
+   * @param string $fileName
+   */
+  public static function sendDownloadDialog($fileOrString, $fileName)
+  {
+    $disposition = (false !== strpos(Registry::get('env')->getUserAgent(), 'MSIE 5.5')) ? '' : 'attachment; ';
+
+    header("Pragma: public");
+    header("Expires: 0");
+    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+    header("Cache-Control: private", false);
+    header("Content-Disposition: " . $disposition . "filename=" . $fileName . ";");
+
+    if (is_file($fileOrString)) {
+      readfile($fileOrString);
+    } else {
+      echo $fileOrString;
+    }
+    exit(0);
+  }
+}

+ 70 - 0
php-pimf/pimf-framework/core/Pimf/Util/Header/ContentType.php

@@ -0,0 +1,70 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf\Util\Header;
+
+/**
+ * Manages a raw HTTP header ContentType sending.
+ *
+ * @package Util_Header
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class ContentType extends ResponseStatus
+{
+  public static function contentTypeJson()
+  {
+    self::type('application/json; charset=utf-8');
+  }
+
+  public static function contentTypePdf()
+  {
+    self::type('application/pdf');
+  }
+
+  public static function contentTypeCsv()
+  {
+    self::type('text/csv');
+  }
+
+  public static function contentTypeTextPlain()
+  {
+    self::type('text/plain');
+  }
+
+  public static function contentTypeTextHTML()
+  {
+    self::type('text/html');
+  }
+
+  public static function contentTypeZip()
+  {
+    self::type('application/zip');
+  }
+
+  public static function contentTypeXZip()
+  {
+    self::type('application/x-zip');
+  }
+
+  public static function contentTypeMSWord()
+  {
+    self::type('application/msword');
+  }
+
+  public static function contentTypeOctetStream()
+  {
+    self::type('application/octet-stream');
+  }
+
+  /**
+   * @param string $definition
+   */
+  public static function type($definition)
+  {
+    header('Content-Type: ' . $definition, true);
+  }
+}

+ 149 - 0
php-pimf/pimf-framework/core/Pimf/Util/Header/ResponseStatus.php

@@ -0,0 +1,149 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf\Util\Header;
+
+use Pimf\Registry;
+
+/**
+ * Manages a raw HTTP header ResponseStatus sending.
+ *
+ * @package Util_Header
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class ResponseStatus
+{
+  /**
+   * @param int    $code    HTTP response code
+   * @param string $status  The header string which will be used to figure out the HTTP status code to send.
+   * @param bool   $replace Whether the header should replace a previous similar header.
+   */
+  public static function send($code, $status, $replace = true)
+  {
+    header('' . Registry::get('env')->SERVER_PROTOCOL . ' ' . $code . ' ' . $status, $replace, $code);
+  }
+
+  public static function sendXFrameDeny()
+  {
+    header('X-Frame-Options: DENY');
+  }
+
+  public static function sendXFrameSameOrigin()
+  {
+    header('X-Frame-Options: SAMEORIGIN');
+  }
+
+  public static function sendContinue()
+  {
+    self::send(100, 'Continue');
+  }
+
+  public static function sendProcessing()
+  {
+    self::send(102, 'Processing');
+  }
+
+  public static function sendOK()
+  {
+    self::send(200, 'OK');
+  }
+
+  public static function sendCreated()
+  {
+    self::send(201, 'Created');
+  }
+
+  public static function sendAccepted()
+  {
+    self::send(202, 'Accepted');
+  }
+
+  public static function sendNoAuthInfo()
+  {
+    self::send(203, 'Non-Authoritative Information');
+  }
+
+  public static function sendNoContent()
+  {
+    self::send(204, 'No Content');
+  }
+
+  public static function sendMovedPermanently()
+  {
+    self::send(301, 'Moved Permanently');
+  }
+
+  public static function sendFound()
+  {
+    self::send(302, 'Found');
+  }
+
+  public function sendNotModified()
+  {
+    self::send(304, 'Not Modified');
+  }
+
+  public static function sendTemporaryRedirect()
+  {
+    self::send(307, 'Temporary Redirect');
+  }
+
+  public static function sendBadRequest()
+  {
+    self::send(400, 'Bad Request');
+  }
+
+  public static function sendUnauthorized()
+  {
+    self::send(401, 'Unauthorized');
+  }
+
+  public static function sendPaymentRequired()
+  {
+    self::send(402, 'Payment Required');
+  }
+
+  public static function sendForbidden()
+  {
+    self::send(403, 'Forbidden');
+  }
+
+  public static function sendMethodNotAllowed()
+  {
+    self::send(405, 'Method Not Allowed');
+  }
+
+  public static function sendNotAcceptable()
+  {
+    self::send(406, 'Not Acceptable');
+  }
+
+  public static function sendProxyAuthRequired()
+  {
+    self::send(407, 'Proxy Authentication Required');
+  }
+
+  public static function sendRequestTimeout()
+  {
+    self::send(408, 'Request Timeout');
+  }
+
+  public static function sendUnsupportedMediaType()
+  {
+    self::send(415, 'Unsupported Media Type');
+  }
+
+  public static function sendLocked()
+  {
+    self::send(423, 'Locked');
+  }
+
+  public static function sendServiceUnavailable()
+  {
+    self::send(503, 'Service Unavailable');
+  }
+}

+ 108 - 0
php-pimf/pimf-framework/core/Pimf/Util/Identifier.php

@@ -0,0 +1,108 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * Identifier util for unified resource generation.
+ *
+ * <code>
+ * $identifier = new class Identifier(1, '23', 123, 'ZZ-TOP', 'Some_Class_name');
+ *
+ * print $identifier; // --> '1_23_123_zz_top_some_class_name'
+ *
+ * $identifier->setDelimiter('/');
+ *
+ * print $identifier->generate(); // --> '1/23/123/zz/top/some/class/name'
+ * </code>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Identifier
+{
+  /**
+   * @var string
+   */
+  protected static $delimiter = '_';
+
+  /**
+   * @var array
+   */
+  protected $args = array();
+
+  /**
+   * Create a new Cache Identifier based on the given parameters.
+   * Integer and string but not array and objects.
+   *
+   * @throws \BadMethodCallException If no identifiers received.
+   */
+  public function __construct()
+  {
+    $args = func_get_args();
+
+    if (!count($args) || !implode('', $args)) {
+      throw new \BadMethodCallException('No identifiers received');
+    }
+
+    $this->args = $args;
+  }
+
+  /**
+   * Return String representation of this Cache Identifier.
+   *
+   * @return string
+   */
+  public function __toString()
+  {
+    return $this->generate();
+  }
+
+  /**
+   * @return string
+   */
+  public function generate()
+  {
+    return (string)$this->slag();
+  }
+
+  /**
+   * Slags the identifier.
+   *
+   * @return string
+   */
+  protected function slag()
+  {
+    $ident = str_replace('-', '_', implode(self::getDelimiter(), $this->args));
+    $ident = str_replace('_', self::getDelimiter(), $ident);
+    $ident = trim($ident);
+    $ident = str_replace(' ', '', $ident);
+
+    return strip_tags(strtolower($ident));
+  }
+
+  /**
+   * Set the delimiter used to create a Cache Identifier.
+   *
+   * @param string $delimiter The delimiter character.
+   */
+  public function setDelimiter($delimiter)
+  {
+    self::$delimiter = $delimiter;
+  }
+
+  /**
+   * Get the delimiter used to create a Cache Identifier.
+   *
+   * @return string
+   */
+  public function getDelimiter()
+  {
+    return self::$delimiter;
+  }
+}

+ 96 - 0
php-pimf/pimf-framework/core/Pimf/Util/IdentityMap.php

@@ -0,0 +1,96 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * The identity map pattern is a database access design pattern used to improve performance
+ * by providing a context-specific, in-memory cache to prevent duplicate retrieval of the
+ * same object data from the database.
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class IdentityMap
+{
+  /**
+   * @var \ArrayObject
+   */
+  protected $idToObject;
+
+  /**
+   * @var \SplObjectStorage
+   */
+  protected $objectToId;
+
+  public function __construct()
+  {
+    $this->objectToId = new \SplObjectStorage();
+    $this->idToObject = new \ArrayObject();
+  }
+
+  /**
+   * @param integer $key
+   * @param mixed   $object
+   */
+  public function set($key, $object)
+  {
+    $this->idToObject[$key]    = $object;
+    $this->objectToId[$object] = $key;
+  }
+
+  /**
+   * @param mixed $object
+   *
+   * @throws \OutOfBoundsException
+   * @return integer
+   */
+  public function getId($object)
+  {
+    if (false === $this->hasObject($object)) {
+      throw new \OutOfBoundsException('no object=' . get_class($object) . ' at the identity-map');
+    }
+
+    return $this->objectToId[$object];
+  }
+
+  /**
+   * @param integer $key
+   *
+   * @return boolean
+   */
+  public function hasId($key)
+  {
+    return isset($this->idToObject[$key]);
+  }
+
+  /**
+   * @param mixed $object
+   *
+   * @return boolean
+   */
+  public function hasObject($object)
+  {
+    return isset($this->objectToId[$object]);
+  }
+
+  /**
+   * @param integer $key
+   *
+   * @throws \OutOfBoundsException
+   * @return object
+   */
+  public function getObject($key)
+  {
+    if (false === $this->hasId($key)) {
+      throw new \OutOfBoundsException('no id=' . $key . ' at the identity-map');
+    }
+
+    return $this->idToObject[$key];
+  }
+}

+ 81 - 0
php-pimf/pimf-framework/core/Pimf/Util/Json.php

@@ -0,0 +1,81 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Json
+{
+  /**
+   * Returns the JSON representation of a value.
+   *
+   * @param mixed $data
+   *
+   * @return string
+   */
+  public static function encode($data)
+  {
+    $json = json_encode($data);
+
+    self::handleError(json_last_error());
+
+    return $json;
+  }
+
+  /**
+   * Decodes a JSON string.
+   *
+   * @param string  $jsonString
+   * @param boolean $assoc If should be converted into associative array/s.
+   *
+   * @return mixed
+   */
+  public static function decode($jsonString, $assoc = false)
+  {
+    $json = json_decode($jsonString, $assoc);
+
+    self::handleError(json_last_error());
+
+    return $json;
+  }
+
+  /**
+   * @param int $status
+   *
+   * @throws \RuntimeException
+   */
+  protected static function handleError($status)
+  {
+    $msg = '';
+
+    switch ($status) {
+      case JSON_ERROR_DEPTH:
+        $msg = 'Maximum stack depth exceeded';
+        break;
+      case JSON_ERROR_STATE_MISMATCH:
+        $msg = 'Underflow or the modes mismatch';
+        break;
+      case JSON_ERROR_CTRL_CHAR:
+        $msg = 'Unexpected control character found';
+        break;
+      case JSON_ERROR_SYNTAX:
+        $msg = 'Syntax error, malformed JSON';
+        break;
+      case 5: //alias for JSON_ERROR_UTF8 due to Availability PHP 5.3.3
+        $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
+        break;
+    }
+
+    if ($msg !== '') {
+      throw new \RuntimeException($msg);
+    }
+  }
+}

+ 249 - 0
php-pimf/pimf-framework/core/Pimf/Util/Ldap.php

@@ -0,0 +1,249 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+use Pimf\Registry;
+
+/**
+ * Wrapper for Lightweight Directory Access Protocol and for a access to "Directory Servers"
+ *
+ * For use please add the following to the end of the config.app.php file:
+ *
+ * <code>
+ *
+ * 'ldap' => array(
+ *
+ *    // where in the directory tree to be started for specific objects searching (is optional)
+ *    'basedn' => 'dc=example,dc=com'
+ *
+ *    // hostname of the domain controller
+ *    'host' => 'dc',
+ *
+ *    // the domain name
+ *    'domain' => 'example.com',
+ *
+ *    // optionally require users to be in this group
+ *    //'group' => 'AppUsers',
+ *
+ *    // domain credentials the app should use to validate users
+ *    // this user does not need any privileges - it's just used to connect to the DC.
+ *    'user' => 'ldap-user_here',
+ *    'password' => 'ldap-password-here',
+ * ),
+ *
+ * </code>
+ *
+ * @link    http://www.php.net/manual/en/intro.ldap.php
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Ldap
+{
+  /**
+   * @var resource A positive LDAP link identifier
+   */
+  protected $conn;
+
+  public function __construct()
+  {
+    if (!function_exists('ldap_connect')) {
+      throw new \RuntimeException('LDAP-auth requires the php-ldap extension to be installed');
+    }
+  }
+
+  public function __destruct()
+  {
+    if (is_resource($this->conn)) {
+      ldap_unbind($this->conn);
+    }
+  }
+
+  /**
+   * Get the current user of the application.
+   *
+   * @param $userDN
+   *
+   * @return null|Ldap\User
+   */
+  public function retrieve($userDN)
+  {
+    if (!is_resource($this->conn)) {
+      $config = Registry::get('ldap');
+      $this->connect($config['user'], $config['password']);
+    }
+
+    return $this->getUser($userDN);
+  }
+
+  /**
+   * Attempt to log a user into the application.
+   *
+   * @param $username
+   * @param $password
+   *
+   * @return Ldap\User
+   */
+  public function attempt($username, $password)
+  {
+    $config = Registry::get('ldap');
+
+    return $this->login($username, $password, $config['group']);
+  }
+
+  /**
+   * @param $user
+   * @param $password
+   *
+   * @throws \RuntimeException
+   */
+  protected function connect($user, $password)
+  {
+    $config = Registry::get('ldap');
+
+    // guess base DN from domain
+    if (!isset($config['basedn'])) {
+      $length           = strrpos($config['domain'], '.');
+      $config['basedn'] = sprintf(
+        'dc=%s,dc=%s', substr($config['domain'], 0, $length), substr($config['domain'], $length + 1)
+      );
+
+      // override the basedn
+      Registry::set('ldap', $config);
+    }
+
+    // connect to the controller
+    if (!$this->conn = ldap_connect("ldap://{$config['host']}.{$config['domain']}")) {
+      throw new \RuntimeException("could not connect to LDAP host {$config['host']}.{$config['domain']}");
+    }
+
+    // required for Windows AD
+    ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3);
+    ldap_set_option($this->conn, LDAP_OPT_REFERRALS, 0);
+
+    // try to authenticate
+    if (!@ldap_bind($this->conn, "{$user}@{$config['domain']}", $password)) {
+      throw new \RuntimeException('could not bind to AD: ' . "{$user}@{$config['domain']}");
+    }
+  }
+
+  /**
+   * @param      $user
+   * @param      $password
+   * @param null $group
+   *
+   * @return Ldap\User
+   * @throws \RuntimeException
+   */
+  protected function login($user, $password, $group = null)
+  {
+    $this->connect($user, $password);
+
+    $config      = Registry::get('ldap');
+    $groupObject = $this->getAccount($group, $config['basedn']);
+    $userObject  = $this->getAccount($user, $config['basedn']);
+
+    if ($group && !$this->checkGroup($userObject['dn'], $groupObject['dn'])) {
+      throw new \RuntimeException('user is not part of the "' . $group . '" group.');
+    }
+
+    return $this->fetch($userObject);
+  }
+
+  /**
+   * @param array $user
+   *
+   * @return Ldap\User
+   * @throws \RuntimeException
+   */
+  protected function fetch(array $user)
+  {
+    if (!isset($user['cn'][0])) {
+      throw new \RuntimeException('not a valid user object');
+    }
+
+    return Ldap\User::factory($user);
+  }
+
+  /**
+   * Searches the LDAP tree for the specified account or group
+   *
+   * @param $account
+   * @param $basedn
+   *
+   * @return null|array
+   */
+  protected function getAccount($account, $basedn)
+  {
+    $result = ldap_search(
+      $this->conn, $basedn, "(samaccountname={$account})", array('dn', 'givenname', 'sn', 'cn', 'memberof', 'objectguid')
+    );
+
+    if (!$result) {
+      return null;
+    }
+
+    $entries = ldap_get_entries($this->conn, $result);
+
+    if ($entries['count'] > 0) {
+      return $entries[0];
+    }
+  }
+
+  /**
+   * Checks group membership of the user, searching
+   * in the specified group and its children (recursively)
+   *
+   * @param $userDN
+   * @param $groupDN
+   *
+   * @return bool
+   * @throws \RuntimeException
+   */
+  public function checkGroup($userDN, $groupDN)
+  {
+    if (!$user = $this->getUser($userDN)) {
+      throw new \RuntimeException('invalid user DN');
+    }
+
+    $memberof = $user->getMemberof();
+
+    for ($i = 0; $i < $memberof['count']; $i++) {
+      if ($groupDN == $memberof[$i]) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * @param $userDN
+   *
+   * @return null|Ldap\User
+   * @throws \RuntimeException
+   */
+  public function getUser($userDN)
+  {
+    if (!is_resource($this->conn)) {
+      throw new \RuntimeException('no LDAP connection bound');
+    }
+
+    if (!$result = ldap_read($this->conn, $userDN, '(objectclass=*)')) {
+      return null;
+    }
+
+    $entries = ldap_get_entries($this->conn, $result);
+
+    if (!$entries['count']) {
+      return null;
+    }
+
+    return $this->fetch($entries[0]);
+  }
+}

+ 132 - 0
php-pimf/pimf-framework/core/Pimf/Util/Ldap/User.php

@@ -0,0 +1,132 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util\Ldap;
+
+/**
+ * Lightweight LDAP user object.
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class User
+{
+  /**
+   * @var string Distinguished Name
+   */
+  protected $dname;
+
+  /**
+   * @var string Exactly the same as CN.
+   */
+  protected $name;
+
+  /**
+   * @var string
+   */
+  protected $firstname;
+
+  /**
+   * @var string
+   */
+  protected $lastname;
+
+  /**
+   * @var string Global Unique Identity Code
+   */
+  protected $objectguid;
+
+  /**
+   * @var array List of groups
+   */
+  protected $memberof;
+
+  /**
+   * @param       $dname
+   * @param       $firstname
+   * @param       $lastname
+   * @param array $memberof
+   * @param       $name
+   * @param       $objectguid
+   */
+  public function __construct($dname, $firstname, $lastname, array $memberof, $name, $objectguid)
+  {
+    $this->dname      = '' . $dname;
+    $this->firstname  = '' . $firstname;
+    $this->lastname   = '' . $lastname;
+    $this->memberof   = (array)$memberof;
+    $this->name       = '' . $name;
+    $this->objectguid = '' . $objectguid;
+  }
+
+  /**
+   * @param array $user
+   *
+   * @return \Pimf\Util\Ldap\User
+   */
+  public static function factory(array $user)
+  {
+    return new self($user['dn'], $user['givenname'][0], $user['sn'][0], isset($user['memberof']) ? $user['memberof']
+        : array('count' => 0), $user['cn'][0], $user['objectguid'][0]);
+  }
+
+  /**
+   * @codeCoverageIgnore
+   * @return string
+   */
+  public function getDname()
+  {
+    return $this->dname;
+  }
+
+  /**
+   * @codeCoverageIgnore
+   * @return string
+   */
+  public function getFirstname()
+  {
+    return $this->firstname;
+  }
+
+  /**
+   * @codeCoverageIgnore
+   * @return string
+   */
+  public function getLastname()
+  {
+    return $this->lastname;
+  }
+
+  /**
+   * @codeCoverageIgnore
+   * @return array
+   */
+  public function getMemberof()
+  {
+    return $this->memberof;
+  }
+
+  /**
+   * @codeCoverageIgnore
+   * @return string
+   */
+  public function getName()
+  {
+    return $this->name;
+  }
+
+  /**
+   * @codeCoverageIgnore
+   * @return string
+   */
+  public function getObjectguid()
+  {
+    return $this->objectguid;
+  }
+
+}

+ 81 - 0
php-pimf/pimf-framework/core/Pimf/Util/LineByLine.php

@@ -0,0 +1,81 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * Process file line by line.
+ *
+ * This is slower than reading in the file all at once, but considerably reduces memory usage!
+ *
+ * <code>
+ *
+ * $linebyline = new LineByLine(
+ *   function ($line) {
+ *     // do what you want with the line.
+ *    return $line;
+ *   }
+ * );
+ *
+ * $feedback = $linebyline->read('//path/to/your/file.txt');
+ *
+ * </code>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class LineByLine
+{
+  protected $callback;
+
+  /**
+   * @param string|array|\Closure $callback
+   *
+   * @throws \RuntimeException If no callable callback given.
+   */
+  public function __construct($callback)
+  {
+    if (!is_callable($callback)) {
+      throw new \RuntimeException('no callable given');
+    }
+
+    $this->callback = $callback;
+  }
+
+  /**
+   * @param string $file     Filename or stream
+   * @param bool   $feedback Should callback responses be collected.
+   * @param bool   $binary   Is binary file.
+   *
+   * @return array List of collected responses.
+   * @throws \RuntimeException If can not create file handle.
+   */
+  public function read($file, $feedback = false, $binary = false)
+  {
+    if (!is_resource($handle = fopen($file, ($binary === true ? 'rb' : 'r')))) {
+      throw new \RuntimeException('can not read handle');
+    }
+
+    $responses = array();
+
+    while (!feof($handle)) {
+
+      $response = call_user_func_array(
+        $this->callback, array(fgets($handle, 4096))
+      );
+
+      if ($feedback === true) {
+        $responses[] = $response;
+      }
+    }
+
+    fclose($handle);
+
+    return $responses;
+  }
+}

+ 140 - 0
php-pimf/pimf-framework/core/Pimf/Util/Message.php

@@ -0,0 +1,140 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * Responsible for general message formatting, used for message flashing or with combination with the translator.
+ *
+ * <code>
+ * $message = new Message(
+ *   'Hello %your_name my name is %my_name! '
+ *    .'I am %my_age, how old are you? I like %object!'
+ * );
+ *
+ * $message->bind('your_name', 'Ben')
+ *         ->bind('my_name', 'Matt')
+ *         ->bind('my_age', '21')
+ *         ->bind('object', 'food');
+ *
+ * print $message;
+ *
+ * .. or ..
+ *
+ * $msg = $message->format();
+ *
+ * .. output will be = "Hello Ben my name is Matt! I am 21, how old are you? I like food!"
+ * </code>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Message
+{
+  /**
+   * @var string The message.
+   */
+  protected $message = '';
+
+  /**
+   * @var array A list of tokes which should be bind.
+   */
+  protected $bindings = array();
+
+  /**
+   * @var string The prefixed delimiter for the tokens.
+   */
+  protected $delimiter = '%';
+
+  /**
+   * @param string $message  The message or the resource.
+   * @param array  $bindings (Optional) A List of tokes whitch should be bind.
+   */
+  public function __construct($message, array $bindings = array())
+  {
+    $this->setMessage($message);
+    $this->bindings = $bindings;
+  }
+
+  /**
+   * @return string
+   */
+  public function getMessage()
+  {
+    return $this->message;
+  }
+
+  /**
+   * @param string $message The message.
+   *
+   * @return Message
+   */
+  public function setMessage($message)
+  {
+    $this->message = $message;
+
+    return $this;
+  }
+
+  /**
+   * @param string $char The character for the prexied delimitation of the tokens.
+   *
+   * @return Message
+   */
+  public function setDelimiter($char)
+  {
+    $this->delimiter = $char;
+
+    return $this;
+  }
+
+  /**
+   * Sets/Updates the value for the given token.
+   *
+   * @param string $token The token.
+   * @param string $value The value for replacement.
+   *
+   * @return Message
+   */
+  public function bind($token, $value)
+  {
+    $this->bindings[$token] = $value;
+
+    return $this;
+  }
+
+  /**
+   * If the object is treated as string.
+   *
+   * @return string
+   */
+  public function __toString()
+  {
+    return (string) $this->format();
+  }
+
+  /**
+   * Retuns formated message.
+   *
+   * @return string
+   */
+  public function format()
+  {
+    if ($this->getMessage() == '' || !$this->getMessage()) {
+      return '';
+    }
+
+    if (count($this->bindings) > 0) {
+      foreach ($this->bindings as $token => $value) {
+        $this->message = str_replace($this->delimiter . $token, $value, $this->message);
+      }
+    }
+
+    return $this->message;
+  }
+}

+ 142 - 0
php-pimf/pimf-framework/core/Pimf/Util/Serializer.php

@@ -0,0 +1,142 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * Due to PHP Bug #39736 - serialize() consumes insane amount of RAM.
+ *
+ * Now we can put objects, strings, integers or arrays. Even instances of SimpleXMLElement can be put too!
+ *
+ * @package Util
+ * @link    https://bugs.php.net/bug.php?id=39736
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Serializer
+{
+  /**
+   * Serialize things.
+   *
+   * @param mixed $object Item you want - string, array, integer, object
+   *
+   * @return string Containing a byte-stream representation.
+   */
+  public static function serialize($object)
+  {
+    $masked = false;
+
+    if (false === is_object($object)) {
+      $object = self::mask($object);
+      $masked = true;
+    }
+
+    $capsule         = new \stdClass();
+    $capsule->type   = get_class($object);
+    $capsule->object = $object;
+    $capsule->fake   = $masked;
+
+    if ($object instanceof \SimpleXMLElement) {
+      $capsule->object = $object->asXml();
+    }
+
+    return '' . self::serializeNative($capsule);
+  }
+
+  /**
+   * Unserialize things.
+   *
+   * @param string $object Serialized object.
+   *
+   * @return mixed
+   */
+  public static function unserialize($object)
+  {
+    $capsule = self::unserializeNative($object);
+
+    if (true === $capsule->fake) {
+      $capsule->object = self::unmask($capsule->object);
+    }
+
+    if ($capsule->type == 'SimpleXMLElement') {
+      $capsule->object = simplexml_load_string($capsule->object);
+    }
+
+    return $capsule->object;
+  }
+
+  /**
+   * @param \stdClass $value Item value.
+   *
+   * @throws \RuntimeException If error during serialize.
+   * @return string
+   */
+  public static function serializeNative($value)
+  {
+    $ret = (extension_loaded('igbinary') && function_exists('igbinary_serialize')) ? @igbinary_serialize($value) : @serialize($value);
+
+    self::bombIf($ret);
+
+    return $ret;
+  }
+
+  /**
+   * @param string $serialized The serialized item-string.
+   *
+   * @throws \RuntimeException If error during unserialize.
+   * @return mixed
+   */
+  public static function unserializeNative($serialized)
+  {
+    $ret = (extension_loaded('igbinary') && function_exists('igbinary_unserialize'))
+      ? @igbinary_unserialize($serialized)
+      : @unserialize(
+        $serialized
+      );
+
+    self::bombIf($ret);
+
+    return $ret;
+  }
+
+  /**
+   * @param mixed $item Item
+   *
+   * @return \stdClass
+   */
+  private static function mask($item)
+  {
+    return (object)$item;
+  }
+
+  /**
+   * @param mixed $item Item
+   *
+   * @return array
+   */
+  private static function unmask($item)
+  {
+    if (isset($item->scalar)) {
+      return $item->scalar;
+    }
+
+    return (array)$item;
+  }
+
+  /**
+   * @param boolean $valid
+   *
+   * @throws \RuntimeException
+   */
+  private static function bombIf($valid)
+  {
+    if ($valid === false) {
+      $err = error_get_last();
+      throw new \RuntimeException($err['message']);
+    }
+  }
+}

+ 281 - 0
php-pimf/pimf-framework/core/Pimf/Util/String.php

@@ -0,0 +1,281 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * String
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class String
+{
+  /**
+   * Check value to find if it was serialized.
+   *
+   * @param   mixed $string Value to check to see if was serialized
+   *
+   * @return  bool
+   */
+  public static function isSerialized($string)
+  {
+    return (@unserialize($string) !== false || $string == 'b:0;');
+  }
+
+  /**
+   * Check for invalid UTF8 encoding and invalid byte .
+   *
+   * @param string $string Your string.
+   *
+   * @return boolean
+   */
+  public static function checkUtf8Encoding($string)
+  {
+    if (!mb_check_encoding($string, 'UTF-8') or
+      !$string == mb_convert_encoding(mb_convert_encoding($string, 'UTF-32', 'UTF-8'), 'UTF-8', 'UTF-32')
+    ) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Ensure that a string is ends with a special string.
+   *
+   * <code>
+   * - ensureTrailing('/', 'http://www.example.com') -> 'http://www.example.com/'
+   * - ensureTrailing('/', 'http://www.example.com/') -> 'http://www.example.com/'
+   * </code>
+   *
+   * @param string $needle   The needle.
+   * @param string $haystack The haystack.
+   *
+   * @return string
+   */
+  public static function ensureTrailing($needle, $haystack)
+  {
+    $needleLength = strlen($needle);
+    $needlePart   = substr($haystack, -1 * $needleLength);
+
+    if ($needlePart !== $needle) {
+      // append missing trailing character.
+      $haystack .= $needle;
+    }
+
+    return $haystack;
+  }
+
+  /**
+   * Ensure that a string is starts with a special string.
+   *
+   * <code>
+   * - ensureLeading('#', '1#2#3#4#5') -> '#1#2#3#4#5'
+   * - ensureLeading('#', '#1#2#3#4#5') -> '#1#2#3#4#5'
+   * </code>
+   *
+   * @param string $needle   The needle.
+   * @param string $haystack The haystack
+   *
+   * @return string
+   */
+  public static function ensureLeading($needle, $haystack)
+  {
+    $needleLength = strlen($needle);
+    $needlePart   = substr($haystack, 0, $needleLength);
+
+    if ($needlePart !== $needle) {
+      // append missing trailing string
+      $haystack = $needle . $haystack;
+    }
+
+    return $haystack;
+  }
+
+  /**
+   * Delete trailing characters.
+   *
+   * <code>
+   * - deleteTrailing('|', '|1|2|3|4|5|')               -> '|1|2|3|4|5'
+   * - deleteTrailing(array('|','5'), '|1|2|3|4|5|555') -> '|1|2|3|4'
+   * </code>
+   *
+   * @param string|array $needle   The needle.
+   * @param string       $haystack The haystack.
+   *
+   * @return string
+   */
+  public static function deleteTrailing($needle, $haystack)
+  {
+    $pattern = '#(' . self::pregQuote($needle, '#') . ')+$#';
+    $result  = preg_replace($pattern, '', $haystack);
+
+    return $result;
+  }
+
+  /**
+   * Delete leading characters.
+   *
+   * <code>
+   * - deleteTrailing('#', '#1#2#3#4#5')             -> '1#2#3#4#5'
+   * - deleteTrailing(array('#', '1'), '##11#2#3#4#5') -> '2#3#4#5'
+   * </code>
+   *
+   * @param string|array $needle   The needle.
+   * @param string       $haystack The haystack.
+   *
+   * @return string
+   */
+  public static function deleteLeading($needle, $haystack)
+  {
+    $pattern = '#^(' . self::pregQuote($needle, '#') . ')+#';
+    $result  = preg_replace($pattern, '', $haystack);
+
+    return $result;
+  }
+
+  /**
+   * Wrapper for preg_quote supporting strings and array of strings.
+   *
+   * @param mixed       $values    The values.
+   * @param null|string $delimiter (Optional) The delimiter.
+   *
+   * @return string
+   */
+  public static function pregQuote($values, $delimiter = null)
+  {
+    if (!is_array($values)) {
+      return preg_quote($values, $delimiter);
+    }
+
+    // Case: needle is array
+    foreach ($values as $key => $value) {
+      $values[$key] = preg_quote($value, $delimiter);
+    }
+
+    return implode('|', $values);
+  }
+
+  /**
+   * An aggressive cleaning - all tags and stuff inside will be removed.
+   *
+   * @param string $string The string.
+   *
+   * @return string
+   */
+  public static function cleanAggressive($string)
+  {
+    return \Pimf\Util\String\Clean::aggressive($string);
+  }
+
+  /**
+   * Cleans against XSS.
+   * Info: use it on showing your request data.
+   *
+   * @param string $string  String to check
+   * @param string $charset Character set (default ISO-8859-1)
+   *
+   * @return string $value Sanitized string
+   */
+  public static function cleanXss($string, $charset = 'ISO-8859-1')
+  {
+    return \Pimf\Util\String\Clean::xss($string, $charset);
+  }
+
+  /**
+   * @param int $length
+   *
+   * @return string
+   */
+  public static function random($length = 32)
+  {
+    return substr(
+      str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 5)), 0, $length
+    );
+  }
+
+  /**
+   * Determine if a given string contains a given sub-string.
+   *
+   * @param string       $haystack
+   * @param string|array $needle
+   *
+   * @return bool
+   */
+  public static function contains($haystack, $needle)
+  {
+    foreach ((array)$needle as $n) {
+      if (strpos($haystack, $n) !== false) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Determine if a given string begins with a given value.
+   *
+   * @param string $haystack
+   * @param string $needle
+   *
+   * @return bool
+   */
+  public static function startsWith($haystack, $needle)
+  {
+    return strpos($haystack, $needle) === 0;
+  }
+
+  /**
+   * Determine if a given string ends with a given value.
+   *
+   * @param string $haystack
+   * @param string $needle
+   *
+   * @return bool
+   */
+  public static function endsWith($haystack, $needle)
+  {
+    return $needle == substr($haystack, strlen($haystack) - strlen($needle));
+  }
+
+  /**
+   * Determine if a given string matches a given pattern.
+   *
+   * Asterisks are translated into zero-or-more regular expression wildcards
+   * to make it convenient to check if string such as "library/*".
+   *
+   * @param  string $pattern Pattern or wildcard
+   * @param  string $value
+   *
+   * @return bool
+   */
+  public static function is($pattern, $value)
+  {
+    if ($pattern !== '/') {
+      $pattern = str_replace('*', '(.*)', $pattern) . '\z';
+    } else {
+      $pattern = '^/$';
+    }
+
+    return (bool)preg_match('#' . $pattern . '#', $value);
+  }
+
+  /**
+   * Check if strange things happening.
+   *
+   * @param string $path
+   *
+   * @return bool
+   */
+  public static function isEvilPath($path)
+  {
+    return self::contains($path, array('../', "..\\", '/..', '\..', ' ', '%2e%2e%2f', '%2e%2e%5C', '%2F%2e%2e', '%5C%2e%2e', '%20'));
+  }
+}

+ 119 - 0
php-pimf/pimf-framework/core/Pimf/Util/String/Clean.php

@@ -0,0 +1,119 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf\Util\String;
+
+/**
+ * String
+ *
+ * @package Util_String
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Clean
+{
+  /**
+   * An aggressive cleaning - all tags and stuff inside will be removed.
+   *
+   * @param string $string The string.
+   *
+   * @return string
+   */
+  public static function aggressive($string)
+  {
+    return (string)preg_replace("/<.*?>/", "", (string)$string);
+  }
+
+  /**
+   * Cleans against XSS.
+   *
+   * @param string $string  String to check
+   * @param string $charset Character set (default ISO-8859-1)
+   *
+   * @return string $value Sanitized string
+   */
+  public static function xss($string, $charset = 'ISO-8859-1')
+  {
+    // Remove Null Characters
+    $string = preg_replace(array('/\0+/', '/(\\\\0)+/'), '', $string);
+
+    // Validate standard character entities
+    $string = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "\\1;", $string);
+
+    // Validate UTF16 two byte encoding (x00)
+    $string = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "\\1\\2;", $string);
+
+    // Just in case stuff like this is submitted: <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
+    $string = preg_replace(array("/%u0([a-z0-9]{3})/i", "/%([a-z0-9]{2})/i"), "&#x\\1;", $string);
+
+    // Convert character entities to ASCII
+    if (preg_match_all("/<(.+?)>/si", $string, $matches)) {
+      for ($i = 0; $i < count($matches['0']); $i++) {
+        $string = str_replace(
+          $matches['1'][$i], html_entity_decode($matches['1'][$i], ENT_COMPAT, $charset), $string
+        );
+      }
+    }
+
+    // Convert all tabs to spaces
+    $string = preg_replace("#\t+#", " ", $string);
+
+    // Makes PHP tags safe
+    $string = str_replace(array('<?php', '<?PHP', '<?', '?>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $string);
+
+    // Compact any exploded words
+    $words = array('javascript', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window');
+
+    foreach ($words as $word) {
+      $temp = '';
+      for ($i = 0; $i < strlen($word); $i++) {
+        $temp .= substr($word, $i, 1) . "\s*";
+      }
+
+      $temp   = substr($temp, 0, -3);
+      $string = preg_replace('#' . $temp . '#s', $word, $string);
+      $string = preg_replace('#' . ucfirst($temp) . '#s', ucfirst($word), $string);
+    }
+
+    // Remove disallowed Javascript in links or img tags
+    $string = preg_replace(
+      "#<a.+?href=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>.*?</a>#si", "", $string
+    );
+    $string = preg_replace(
+      "#<img.+?src=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>#si", "", $string
+    );
+    $string = preg_replace("#<(script|xss).*?\>#si", "", $string);
+
+    // Remove JavaScript Event Handlers
+    $string = preg_replace(
+      '#(<[^>]+.*?)(onblur|onchange|onclick|onfocus|onload|onmouseover|onmouseup|'
+      . 'onmousedown|onselect|onsubmit|onunload|onkeypress|onkeydown|onkeyup|onresize)[^>]*>#iU', "\\1>", $string
+    );
+
+    // Sanitize naughty HTML elements
+    $string = preg_replace(
+      '#<(/*\s*)(alert|applet|basefont|base|behavior|bgsound|'
+      . 'blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input'
+      . '|layer|link|meta|object|plaintext|style|script|textarea|title|xml|xss)([^>]*)>#is', "&lt;\\1\\2\\3&gt;", $string
+    );
+
+    // Sanitize naughty scripting elements
+    $string = preg_replace(
+      '#(alert|cmd|passthru|eval|exec|system|fopen|fsockopen|' . 'file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si',
+      "\\1\\2&#40;\\3&#41;", $string
+    );
+
+    // Final clean up
+    $bad = array('document.cookie' => '', 'document.write' => '', 'window.location' => '', "javascript\s*:" => '', "Redirect\s+302" => '',
+                 '<!--'            => '&lt;!--', '-->' => '--&gt;');
+
+    foreach ($bad as $key => $val) {
+      $string = preg_replace("#" . $key . "#i", $val, $string);
+    }
+
+    return $string;
+  }
+}

+ 220 - 0
php-pimf/pimf-framework/core/Pimf/Util/Uploaded.php

@@ -0,0 +1,220 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c) 2010-2013 Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * A file uploaded through a form.
+ *
+ * <code>
+ *
+ *   // Create a file instance.
+ *   $upload = new Uploaded(
+ *     $_FILES['tmp_name'], $_FILES['name'], $_FILES['type'], $_FILES['size'], $_FILES['error']
+ *   );
+ *
+ *   if ($upload instanceof Uploaded) {
+ *     $upload->move('path/to/your/images/dir', $upload->getClientOriginalName());
+ *   }
+ *
+ * </code>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Uploaded extends File
+{
+  /**
+   * Whether the test mode is activated.
+   * Local files are used in test mode hence the code should not enforce HTTP uploads.
+   *
+   * @var bool
+   */
+  private $test = false;
+
+  /**
+   * The original name of the uploaded file.
+   *
+   * @var string
+   */
+  private $name;
+
+  /**
+   * The mime type provided by the uploader.
+   *
+   * @var string
+   */
+  private $mime;
+
+  /**
+   * The file size provided by the uploader.
+   *
+   * @var string
+   */
+  private $size;
+
+  /**
+   * The UPLOAD_ERR_XXX constant provided by the uploader.
+   *
+   * @var integer
+   */
+  private $error;
+
+  /**
+   * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
+   *
+   * <code>
+   *   // Create a file instance.
+   *   $file = new Uploaded(
+   *     $_FILES['tmp_name'], $_FILES['name'], $_FILES['type'], $_FILES['size'], $_FILES['error']
+   *   );
+   * </code>
+   *
+   * @param string      $path  The full temporary path to the file
+   * @param string      $name  The original file name
+   * @param string|null $mime  The type of the file as provided by PHP
+   * @param string|null $size  The file size
+   * @param int|null    $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants)
+   * @param bool        $test  Whether the test mode is active
+   *
+   * @throws \RuntimeException If file_uploads is disabled
+   */
+  public function __construct($path, $name, $mime = null, $size = null, $error = null, $test = false)
+  {
+    if (!ini_get('file_uploads')) {
+      throw new \RuntimeException('Unable to create file because "file_uploads" is disabled in your php.ini');
+    }
+
+    $this->name  = $this->getName($name);
+    $this->mime  = $mime ? : 'application/octet-stream';
+    $this->size  = $size;
+    $this->error = $error ? : UPLOAD_ERR_OK;
+    $this->test  = (bool)$test;
+
+    parent::__construct($path, UPLOAD_ERR_OK === $this->error);
+  }
+
+  /**
+   * Returns the original file name.
+   *
+   * It is extracted from the request from which the file has been uploaded.
+   * Then is should not be considered as a safe value.
+   *
+   * @return string
+   */
+  public function getClientOriginalName()
+  {
+    return $this->name;
+  }
+
+  /**
+   * Returns the file mime type.
+   *
+   * It is extracted from the request from which the file has been uploaded.
+   * Then is should not be considered as a safe value.
+   *
+   * @return string
+   */
+  public function getClientMimeType()
+  {
+    return $this->mime;
+  }
+
+  /**
+   * Returns the file size.
+   *
+   * It is extracted from the request from which the file has been uploaded.
+   * Then is should not be considered as a safe value.
+   *
+   * @return string|null
+   */
+  public function getClientSize()
+  {
+    return $this->size;
+  }
+
+  /**
+   * Returns the upload error.
+   *
+   * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
+   * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
+   *
+   * @return integer
+   */
+  public function getError()
+  {
+    return $this->error;
+  }
+
+  /**
+   * Returns whether the file was uploaded successfully.
+   *
+   * @return boolean True if no error occurred during uploading
+   */
+  public function isValid()
+  {
+    return $this->error === UPLOAD_ERR_OK;
+  }
+
+  /**
+   * Moves the file to a new location.
+   *
+   * @param string $dir
+   * @param null   $name
+   *
+   * @return \Pimf\Util\File
+   * @throws \RuntimeException If the file has not been uploaded via Http or can not move the file.
+   */
+  public function move($dir, $name = null)
+  {
+    if ($this->isValid()) {
+
+      if ($this->test) {
+        return parent::move($dir, $name);
+      }
+
+      if (is_uploaded_file($this->getPathname())) {
+
+        $target = $this->getTargetFile($dir, $name);
+
+        if (!@move_uploaded_file($this->getPathname(), $target)) {
+          $error = error_get_last();
+          throw new \RuntimeException("Could not move the file {$this->getPathname()} to $target ({$error['message']})");
+        }
+
+        @chmod($target, 0666 & ~umask());
+
+        return $target;
+      }
+    }
+
+    throw new \RuntimeException("The file {$this->getPathname()} has not been uploaded via Http");
+  }
+
+  /**
+   * Returns the maximum size of an uploaded file in bytes as configured in php.ini
+   *
+   * @return int
+   */
+  public static function getMaxFilesize()
+  {
+    $max = trim(ini_get('upload_max_filesize'));
+
+    if ('' === $max) {
+      return PHP_INT_MAX;
+    }
+
+    $unit = strtolower(substr($max, -1));
+
+    if (in_array($unit, array('g', 'm', 'k'), true)) {
+      $max *= 1024;
+    }
+
+    return (integer)$max;
+  }
+}

+ 118 - 0
php-pimf/pimf-framework/core/Pimf/Util/Uploaded/Factory.php

@@ -0,0 +1,118 @@
+<?php
+/**
+ * Util\Uploaded
+ *
+ * @copyright Copyright (c) 2010-2013 Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+namespace Pimf\Util\Uploaded;
+
+use \Pimf\Util\Uploaded;
+
+/**
+ * A file uploaded through a form.
+ *
+ * <code>
+ *
+ *   // Create an instance using the factory method for more security.
+ *   $upload = \Pimf\Util\Uploaded\Factory::get($_FILES);
+ *
+ *   if ($upload instanceof Uploaded) {
+ *     $upload->move('path/to/your/images/dir', $upload->getClientOriginalName());
+ *   }
+ *
+ * </code>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Factory
+{
+  /**
+   * @var array
+   */
+  protected static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
+
+  /**
+   * Factory for save instance creation.
+   *
+   * <code>
+   *   // Create an instance using the factory method.
+   *   $file = \Pimf\Util\Uploaded\Factory::get($_FILES);
+   * </code>
+   *
+   * @param mixed $file A $_FILES multi-dimensional array of uploaded file information.
+   * @param bool  $test Whether the test mode is active for essayer unit-testing.
+   *
+   * @return null|Uploaded
+   */
+  public static function get(array $file, $test = false)
+  {
+    $file = static::heal($file);
+
+    if (is_array($file) && isset($file['name']) && empty($file['name']) === false) {
+
+      $keys = array_keys($file);
+      sort($keys);
+
+      if ($keys == self::$fileKeys) {
+
+        if (UPLOAD_ERR_NO_FILE == $file['error']) {
+          return null;
+        }
+
+        return new Uploaded($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error'], $test);
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Heals a malformed PHP $_FILES array.
+   *
+   * PHP has a bug that the format of the $_FILES array differs, depending on
+   * whether the uploaded file fields had normal field names or array-like
+   * field names ("normal" vs. "parent[child]").
+   *
+   * This method fixes the array to look like the "normal" $_FILES array.
+   *
+   * @param array $data
+   *
+   * @return array
+   */
+  protected static function heal($data)
+  {
+    if (!is_array($data)) {
+      return $data;
+    }
+
+    $keys = array_keys($data);
+    sort($keys);
+
+    if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) {
+      return $data;
+    }
+
+    $files = $data;
+
+    foreach (self::$fileKeys as $k) {
+      unset($files[$k]);
+    }
+
+    foreach (array_keys($data['name']) as $key) {
+      $files[$key] = static::heal(
+        array(
+          'error'    => $data['error'][$key],
+          'name'     => $data['name'][$key],
+          'type'     => $data['type'][$key],
+          'tmp_name' => $data['tmp_name'][$key],
+          'size'     => $data['size'][$key]
+        )
+      );
+    }
+
+    return $files;
+  }
+}
+ 

+ 140 - 0
php-pimf/pimf-framework/core/Pimf/Util/Uuid.php

@@ -0,0 +1,140 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+use Pimf\Registry;
+
+/**
+ * A class that generates RFC 4122 UUIDs
+ *
+ * <pre>
+ * This specification defines a Uniform Resource Name namespace for
+ * UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally
+ * Unique IDentifier).  A UUID is 128 bits long, and requires no central
+ * registration process.
+ * </pre>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ * @see     http://www.ietf.org/rfc/rfc4122.txt
+ */
+final class Uuid
+{
+  /**
+   * 32-bit integer that identifies this host.
+   *
+   * @var integer
+   */
+  private static $node = null;
+
+  /**
+   * Process identifier.
+   *
+   * @var integer
+   */
+  private static $pid = null;
+
+  /**
+   * Returns a 32-bit integer that identifies this host.
+   *
+   * The node identifier needs to be unique among nodes
+   * in a cluster for a given application in order to
+   * avoid collisions between generated identifiers.
+   *
+   * @return integer
+   */
+  private static function getNodeId()
+  {
+    $host     = Registry::get('env')->getIp();
+    $hostname = Registry::get('env')->getHost();
+
+    if ($host === null && true === function_exists('gethostname')) {
+      $hostname = gethostname();
+      $host     = gethostbyname($hostname);
+    }
+
+    if ($host === null && true === function_exists('php_uname')) {
+      $hostname = php_uname('n');
+      $host     = gethostbyname($hostname);
+    }
+
+    if ($host === null && $hostname !== null) {
+      $host = crc32($hostname);
+    }
+
+    if ($host === null) {
+      $host = '127.0.0.1';
+    }
+
+    return ip2long($host);
+  }
+
+  /**
+   * Returns a process identifier.
+   *
+   * In multi-process servers, this should be the system process ID.
+   * In multi-threaded servers, this should be some unique ID to
+   * prevent two threads from generating precisely the same UUID
+   * at the same time.
+   *
+   * @return integer
+   */
+  private static function getLockId()
+  {
+    return getmypid();
+  }
+
+  /**
+   * Generate an RFC 4122 UUID.
+   *
+   * This is pseudo-random UUID influenced by the system clock, IP
+   * address and process ID.
+   *
+   * The intended use is to generate an identifier that can uniquely
+   * identify user generated posts, comments etc. made to a website.
+   * This generation process should be sufficient to avoid collisions
+   * between nodes in a cluster, and between apache children on the
+   * same host.
+   *
+   * @return string
+   */
+  public static function generate()
+  {
+    if (self::$node === null) {
+      self::$node = self::getNodeId();
+    }
+
+    if (self::$pid === null) {
+      self::$pid = self::getLockId();
+    }
+
+    list($time_mid, $time_lo) = explode(' ', microtime());
+
+    $time_low = (int)$time_lo;
+    $time_mid = (int)substr($time_mid, 2);
+
+    $time_and_version = mt_rand(0, 0xfff);
+    /* version 4 UUID */
+    $time_and_version |= 0x4000;
+
+    $clock_seq_low = mt_rand(0, 0xff);
+
+    /* type is pseudo-random */
+    $clock_seq_high = mt_rand(0, 0x3f);
+    $clock_seq_high |= 0x80;
+
+    $node_low = self::$pid;
+    $node     = self::$node;
+
+    return sprintf(
+      '%08x-%04x-%04x-%02x%02x-%04x%08x', $time_low, $time_mid & 0xffff, $time_and_version, $clock_seq_high, $clock_seq_low, $node_low,
+      $node
+    );
+  }
+}

+ 303 - 0
php-pimf/pimf-framework/core/Pimf/Util/Validator.php

@@ -0,0 +1,303 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * Validator
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Validator
+{
+  /**
+   * @var array
+   */
+  protected $errors = array();
+
+  /**
+   * @var \Pimf\Param
+   */
+  protected $attributes;
+
+  /**
+   * @param \Pimf\Param $attributes
+   */
+  public function __construct(\Pimf\Param $attributes)
+  {
+    $this->attributes = $attributes;
+  }
+
+  /**
+   * @return array
+   */
+  public function getErrors()
+  {
+    return $this->errors;
+  }
+
+  /**
+   * @return bool
+   */
+  public function isValid()
+  {
+    return empty($this->errors);
+  }
+
+  /**
+   * check to see if valid email address
+   *
+   * @param string $field
+   *
+   * @return bool
+   */
+  public function email($field)
+  {
+    return (filter_var(trim($this->get($field)), FILTER_VALIDATE_EMAIL) !== false) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check is a valid IP.
+   *
+   * @param $field
+   *
+   * @return bool
+   */
+  public function ip($field)
+  {
+    return (filter_var(trim($this->get($field)), FILTER_VALIDATE_IP) !== false) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check is a valid URL.
+   *
+   * @param $field
+   *
+   * @return bool
+   */
+  public function url($field)
+  {
+    return (filter_var(trim($this->get($field)), FILTER_VALIDATE_URL) !== false) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check to see if two fields are equal.
+   *
+   * @param string $field1
+   * @param string $field2
+   * @param bool   $caseInsensitive
+   *
+   * @return bool
+   */
+  public function compare($field1, $field2, $caseInsensitive = false)
+  {
+    $field1value = $this->get($field1);
+    $field2value = $this->get($field2);
+
+    $valid = (strcmp($field1value, $field2value) == 0);
+
+    if ($caseInsensitive) {
+      $valid = (strcmp(strtolower($field1value), strtolower($field2value)) == 0);
+    }
+
+    return ($valid === true) ? : $this->error($field1 . "|" . $field2, __FUNCTION__);
+  }
+
+  /**
+   * Check to see if the length of a field is between two numbers
+   *
+   * @param string $field
+   * @param int    $min
+   * @param int    $max
+   * @param bool   $inclusive
+   *
+   * @return bool
+   */
+  public function lengthBetween($field, $min, $max, $inclusive = false)
+  {
+    $fieldValue = strlen(trim($this->get($field)));
+
+    $valid = ($fieldValue <= $max && $fieldValue >= $min);
+
+    if (!$inclusive) {
+      $valid = ($fieldValue < $max && $fieldValue > $min);
+    }
+
+    return ($valid === true) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check to see if there is punctuation
+   *
+   * @param string $field
+   *
+   * @return bool
+   */
+  public function punctuation($field)
+  {
+    return (preg_match("/[^\w\s\p{P}]/", '' . $this->get($field)) > 0) ? $this->error($field, __FUNCTION__) : true;
+  }
+
+  /**
+   * length functions on a field takes <, >, ==, <=, and >= as operators.
+   *
+   * @param string $field
+   * @param string $operator
+   * @param int    $length
+   *
+   * @return bool
+   */
+  public function length($field, $operator, $length)
+  {
+    return $this->middleware($field, strlen(trim($this->get($field))), $operator, $length);
+  }
+
+  /**
+   * Number value functions takes <, >, ==, <=, and >= as operators.
+   *
+   * @param string     $field
+   * @param string     $operator
+   * @param string|int $value
+   *
+   * @return bool
+   */
+  public function value($field, $operator, $value)
+  {
+    return $this->middleware($field, $this->get($field), $operator, $value);
+  }
+
+  /**
+   * Check if a number value is between $max and $min
+   *
+   * @param string $field
+   * @param int    $min
+   * @param int    $max
+   * @param bool   $inclusive
+   *
+   * @return bool
+   */
+  public function valueBetween($field, $min, $max, $inclusive = false)
+  {
+    $fieldValue = $this->get($field);
+
+    $valid = ($fieldValue <= $max && $fieldValue >= $min);
+
+    if (!$inclusive) {
+      $valid = ($fieldValue < $max && $fieldValue > $min);
+    }
+
+    return ($valid === true) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check if a field contains only decimal digit
+   *
+   * @param string $field
+   *
+   * @return bool
+   */
+  public function digit($field)
+  {
+    return (ctype_digit((string)$this->get($field)) === true) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check if a field contains only alphabetic characters
+   *
+   * @param string $field
+   *
+   * @return bool
+   */
+  public function alpha($field)
+  {
+    return (ctype_alpha((string)$this->get($field)) === true) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check if a field contains only alphanumeric characters
+   *
+   * @param string $field
+   *
+   * @return bool
+   */
+  public function alphaNumeric($field)
+  {
+    return (ctype_alnum((string)$this->get($field)) === true) ? : $this->error($field, __FUNCTION__);
+  }
+
+  /**
+   * Check if field is a date by specified format.
+   *
+   * @param string $field
+   * @param string $format Find formats here http://www.php.net/manual/en/function.date.php
+   *
+   * @return boolean|null
+   */
+  public function date($field, $format)
+  {
+    $fieldValue = $this->get($field);
+
+    try {
+
+      $date = new \DateTime($fieldValue);
+
+      return $fieldValue === $date->format($format);
+
+    } catch (\Exception $exception) {
+      return $this->error($field, __FUNCTION__);
+    }
+  }
+
+  /**
+   * @param string $field
+   * @param int    $error
+   *
+   * @return boolean
+   */
+  protected function error($field, $error)
+  {
+    $this->errors = array_merge_recursive($this->errors, array($field => $error));
+
+    return false;
+  }
+
+  /**
+   * @param string $attribute
+   *
+   * @return string
+   * @throws \OutOfBoundsException If attribute not at range
+   */
+  protected function get($attribute)
+  {
+    if (!$value = $this->attributes->get($attribute, null, false)) {
+      throw new \OutOfBoundsException('no attribute with name "' . $attribute . '" set');
+    }
+
+    return $value;
+  }
+
+  /**
+   * @param string         $fieldName
+   * @param string         $comparing
+   * @param string         $operator
+   * @param string|integer $expecting
+   *
+   * @return bool
+   */
+  protected function middleware($fieldName, $comparing, $operator, $expecting)
+  {
+    if (in_array($operator, array("<", ">", "==", "<=", ">="), true)) {
+      $func = create_function('$a,$b', 'return ($a ' . '' . $operator . ' $b);');
+
+      return ($func($comparing, $expecting) === true) ? : $this->error($fieldName, $operator);
+    }
+
+    return false;
+  }
+}

+ 68 - 0
php-pimf/pimf-framework/core/Pimf/Util/Validator/Factory.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util\Validator;
+
+use Pimf\Param;
+use Pimf\Util\Validator;
+
+/**
+ * Validator Factory
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+abstract class Factory
+{
+  /**
+   * <code>
+   *
+   *  $attributes = array(
+   *    'fname'    => 'conan',
+   *    'age'      => 33,
+   *   );
+   *
+   *   $rules = array(
+   *     'fname'   => 'alpha|length[>,0]|lengthBetween[1,9]',
+   *     'age'     => 'digit|value[>,18]|value[==,33]',
+   *   );
+   *
+   *  $validator = Validator::factory($attributes, $rules);
+   *
+   * </code>
+   *
+   * @param array|\Pimf\Param $attributes
+   * @param array             $rules
+   *
+   * @return \Pimf\Util\Validator
+   */
+  public static function get($attributes, array $rules)
+  {
+    if (!($attributes instanceof Param)) {
+      $attributes = new Param((array)$attributes);
+    }
+
+    $validator = new Validator($attributes);
+
+    foreach ($rules as $key => $rule) {
+
+      $checks = (is_string($rule)) ? explode('|', $rule) : $rule;
+
+      foreach ($checks as $check) {
+
+        $items      = explode('[', str_replace(']', '', $check));
+        $method     = $items[0];
+        $parameters = array_merge(array($key), (isset($items[1]) ? explode(',', $items[1]) : array()));
+
+        call_user_func_array(array($validator, $method), $parameters);
+      }
+    }
+
+    return $validator;
+  }
+}

+ 132 - 0
php-pimf/pimf-framework/core/Pimf/Util/Value.php

@@ -0,0 +1,132 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * A helper class that provides static methods to convert component property values to specific types.
+ *
+ * For example, a boolean-typed property setter method would be as follows:
+ *
+ * <code>
+ * public function setPropertyName($value)
+ * {
+ *     $value = Value::ensureBoolean($value);
+ *     // $value is now of boolean type
+ * }
+ * </code>
+ *
+ * <pre>
+ * Properties can be of the following types with specific type conversion rules:
+ *
+ * boolean: string 'true' (case-insensitive) will be converted to true,
+ *          string 'false' (case-insensitive) will be converted to false.
+ * array:   string starting with '(' and ending with ')' will be considered as
+ *          as an array expression and will be evaluated. Otherwise, an array
+ *          with the value to be ensured is returned.
+ * </pre>
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Value
+{
+  /**
+   * Converts a value to boolean type.
+   * Note, string like "yes", "on", "true" (case-insensitive) will be converted to true,
+   * string 'false', 'no', 'false' (case-insensitive) will be converted to false.
+   * If a string represents a non-zero number, it will be treated as true.
+   *
+   * @param mixed $value the value to be converted.
+   *
+   * @return boolean
+   */
+  public static function ensureBoolean($value)
+  {
+    return filter_var($value, FILTER_VALIDATE_BOOLEAN);
+  }
+
+  /**
+   * Converts a value to array type. If the value is a string and it is
+   * in the form "[a,b,c]" then an array consisting of each of the elements
+   * will be returned. If the value is a string and it is not in this form
+   * then an array consisting of just the string will be returned. If the value
+   * is not a string then
+   *
+   * @param mixed $value the value to be converted.
+   *
+   * @return array
+   */
+  public static function ensureArray($value)
+  {
+    if (is_object($value)) {
+      // Gets the public properties of the given object.
+      return get_object_vars($value);
+    }
+
+    if ((string)$value === $value) {
+      $value = trim($value);
+      $len   = mb_strlen($value);
+
+      if ($len >= 2 && $value[0] == '[' && $value[$len - 1] == ']') {
+        $slag = str_replace(array('[', ']'), '', $value);
+
+        return explode(',', $slag);
+      }
+
+      return $len > 0 ? array($value) : array();
+    }
+
+    return (array)$value;
+  }
+
+  /**
+   * Converts a value to enum type.
+   *
+   * This method checks if the value is of the specified enumerable type.
+   * A value is a valid enumerable value if it is equal to the name of a constant
+   * in the specified enumerable type (class).
+   *
+   * @param string  $value           The enumerable value to be checked.
+   * @param string  $className       The enumerable class name (make sure it is included before calling this function).
+   * @param boolean $returnEnumValue Sets if whether to enum-name or enum-value be returned.
+   *
+   * @return string The value of the valid enumeration.
+   * @throws \InvalidArgumentException if the value is not a valid enumerable value
+   */
+  public static function ensureEnum($value, $className, $returnEnumValue = true)
+  {
+    static $types;
+
+    if (!isset($types[$className])) {
+      $types[$className] = new \ReflectionClass($className);
+    }
+
+    if ($returnEnumValue === true) {
+      if ($types[$className]->hasConstant($value)) {
+        return $types[$className]->getConstant($value);
+      }
+    }
+
+    if (in_array($value, array_values($types[$className]->getConstants()))) {
+      foreach ($types[$className]->getConstants() as $enumName => $enumValue) {
+        if ($enumValue == $value) {
+          return $enumName;
+        }
+      }
+    }
+
+    throw new \InvalidArgumentException(new Message('Invalid enumerable value "%value". Please make sure it is among (%enum).', array('value' => $value,
+                                                                                                                                      'enum'  => implode(
+                                                                                                                                        ', ',
+                                                                                                                                        $types[$className]->getConstants(
+                                                                                                                                        )
+                                                                                                                                      ))));
+  }
+
+}

+ 115 - 0
php-pimf/pimf-framework/core/Pimf/Util/Xml.php

@@ -0,0 +1,115 @@
+<?php
+/**
+ * Util
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\Util;
+
+/**
+ * An XML util for converting XML to DOMDocument or SimpleXMLElement or to Array.
+ *
+ * @package Util
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class Xml
+{
+  /**
+   * Convert anything DOMDocument|SimpleXMLElement|string to DOMDocument.
+   *
+   * @param \DOMDocument|\SimpleXMLElement|string $xml String may be filename or xml string
+   *
+   * @throws \InvalidArgumentException
+   * @return \DOMDocument
+   */
+  public static function toDOMDocument($xml)
+  {
+    if ($xml instanceof \DOMDocument) {
+      return $xml;
+    }
+
+    if ($xml instanceof \SimpleXMLElement) {
+      $doc = new \DOMDocument();
+      $doc->loadXML('' . $xml->asXML());
+
+      return $doc;
+    }
+
+    if (is_string($xml)) {
+      $doc = new \DOMDocument();
+
+      if (is_file($xml)) {
+        $doc->load($xml);
+
+        return $doc;
+      }
+
+      $doc->loadXML($xml);
+
+      return $doc;
+    }
+
+    $type = is_object($xml) ? get_class($xml) : gettype($xml);
+
+    throw new \InvalidArgumentException("Cannot convert instance of '$type' to DOMDocument");
+  }
+
+  /**
+   * Convert anything DOMDocument|SimpleXMLElement|string to SimpleXMLElement.
+   *
+   * @param \DOMDocument|\SimpleXMLElement|string $xml String may be filename or xml string
+   *
+   * @throws \InvalidArgumentException
+   * @return \SimpleXMLElement
+   */
+  public static function toSimpleXMLElement($xml)
+  {
+    if ($xml instanceof \SimpleXMLElement) {
+      return $xml;
+    }
+
+    if ($xml instanceof \DOMDocument) {
+      return simplexml_import_dom($xml);
+    }
+
+    if (is_string($xml)) {
+
+      if (is_file($xml)) {
+        return simplexml_load_file($xml);
+      }
+
+      return simplexml_load_string($xml);
+    }
+
+    $type = is_object($xml) ? get_class($xml) : gettype($xml);
+
+    throw new \InvalidArgumentException("Cannot convert instance of '$type' to DOMDocument");
+  }
+
+  /**
+   * Convert SimpleXMLElement to multidimensional array.
+   *
+   * @param \SimpleXMLElement $xml
+   * @param string            $namespace The namespace that schould be used.
+   *
+   * @throws \OutOfBoundsException If namespace not found in the xml.
+   * @return array
+   */
+  public static function toArray(\SimpleXMLElement $xml, $namespace = null)
+  {
+    if ($namespace !== null) {
+
+      $namespaces = $xml->getNamespaces();
+
+      if (false === isset($namespaces[$namespace])) {
+        throw new \OutOfBoundsException('namespace [' . $namespace . '] not found');
+      }
+
+      $xml = $xml->children($namespaces[$namespace]);
+    }
+
+    return Json::decode(Json::encode($xml), true);
+  }
+}

+ 190 - 0
php-pimf/pimf-framework/core/Pimf/View.php

@@ -0,0 +1,190 @@
+<?php
+/**
+ * Pimf
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf;
+
+use Pimf\Contracts\Renderable;
+use Pimf\Util\File;
+use Pimf\Contracts\Arrayable;
+
+/**
+ * A simply view for sending and rendering data.
+ *
+ * @package Pimf
+ * @author  Gjero Krsteski <[email protected]>
+ */
+class View implements Renderable
+{
+  /**
+   * @var string Name of the template.
+   */
+  protected $template;
+
+  /**
+   * Contains the variables that are to be embedded in the template.
+   *
+   * @var \ArrayObject
+   */
+  protected $data;
+
+  /**
+   * Path to templates - is framework restriction.
+   *
+   * @var string
+   */
+  protected $path;
+
+  /**
+   * @param string $template
+   * @param array  $data
+   * @param string $path Path to templates if you do not want to use PIMF framework restriction.
+   */
+  public function __construct($template = 'default.phtml', array $data = array(), $path = null)
+  {
+    $conf           = Registry::get('conf');
+    $this->data     = new \ArrayObject($data, \ArrayObject::ARRAY_AS_PROPS);
+    $this->path     = (!$path) ? BASE_PATH . 'app/' . $conf['app']['name'] . '/_templates' : $path;
+    $this->template = (string)$template;
+  }
+
+  /**
+   * @param string $template
+   *
+   * @return View
+   */
+  public function produce($template)
+  {
+    $view           = clone $this;
+    $view->template = (string)$template;
+
+    return $view;
+  }
+
+  /**
+   * @param string          $template
+   * @param array|Arrayable $model
+   *
+   * @return string
+   */
+  public function partial($template, $model = array())
+  {
+    $model = ($model instanceof Arrayable) ? $model->toArray() : $model;
+
+    return $this->produce($template)->pump($model)->render();
+  }
+
+  /**
+   * @param string $template
+   * @param array  $model
+   *
+   * @return string
+   */
+  public function loop($template, array $model = array())
+  {
+    $out = '';
+
+    foreach ($model as $row) {
+      $out .= $this->partial($template, $row);
+    }
+
+    return $out;
+  }
+
+  /**
+   * Assigns a variable to a specific key for the template.
+   *
+   * @param string $key   The key.
+   * @param mixed  $value The Value.
+   *
+   * @return View
+   */
+  public function assign($key, $value)
+  {
+    $this->data[$key] = $value;
+
+    return $this;
+  }
+
+  /**
+   * Exchange all variables.
+   *
+   * @param $model
+   *
+   * @return View
+   */
+  public function pump(array $model)
+  {
+    $this->data->exchangeArray($model);
+
+    return $this;
+  }
+
+  /**
+   * Is utilized for reading data from inaccessible properties.
+   *
+   * @param string $name
+   *
+   * @return mixed|null
+   */
+  public function __get($name)
+  {
+    if ($this->data->offsetExists($name)) {
+      return $this->data->offsetGet($name);
+    }
+
+    $trace = debug_backtrace();
+    trigger_error(
+      'undefined property "' . $name . '" at file ' . $trace[0]['file'] . ' line ' . $trace[0]['line'], E_USER_WARNING
+    );
+
+    return null;
+  }
+
+  /**
+   * @return string
+   * @throws \Exception
+   */
+  public function render()
+  {
+    $level = ob_get_level();
+    ob_start();
+
+    try {
+
+      echo $this->reunite();
+
+    } catch (\Exception $exception) {
+
+      while (ob_get_level() > $level) {
+        ob_end_clean();
+      }
+
+      throw $exception;
+    }
+
+    return ob_get_clean();
+  }
+
+  /**
+   * Puts the template an the variables together.
+   */
+  public function reunite()
+  {
+    include new File(str_replace('/', DS, $this->path . '/' . $this->template));
+  }
+
+  /**
+   * Act when the view is treated like a string
+   *
+   * @return string
+   */
+  public function __toString()
+  {
+    return $this->render();
+  }
+}

+ 75 - 0
php-pimf/pimf-framework/core/Pimf/View/Haanga.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * View
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\View;
+
+use Pimf\Contracts\Reunitable;
+use Pimf\View;
+use Pimf\Registry;
+use Pimf\Util\Value;
+
+/**
+ * A view for HAANGA template engine that uses Django syntax - fast and secure template engine for PHP.
+ *
+ * For use please add the following code to the end of the config.app.php file:
+ *
+ * <code>
+ *
+ * 'view' => array(
+ *
+ *   'haanga' => array(
+ *     'cache'       => true,  // if compilation caching should be used
+ *     'debug'       => false, // if set to true, you can display the generated nodes
+ *     'auto_reload' => true,  // useful to recompile the template whenever the source code changes
+ *  ),
+ *
+ * ),
+ *
+ * </code>
+ *
+ * @link    http://haanga.org/documentation
+ * @package View
+ * @author  Gjero Krsteski <[email protected]>
+ * @codeCoverageIgnore
+ */
+class Haanga extends View implements Reunitable
+{
+  /**
+   * @param string $template
+   * @param array  $data
+   */
+  public function __construct($template, array $data = array())
+  {
+    parent::__construct($template, $data);
+
+    $conf = Registry::get('conf');
+
+    $options = array('debug'    => Value::ensureBoolean($conf['view']['haanga']['debug']), 'template_dir' => $this->path,
+                     'autoload' => Value::ensureBoolean($conf['view']['haanga']['auto_reload']),);
+
+    if ($conf['view']['haanga']['cache'] === true) {
+      $options['cache_dir'] = $this->path . '/haanga_cache';
+    }
+
+    require_once BASE_PATH . "Haanga/lib/Haanga.php";
+
+    \Haanga::configure($options);
+  }
+
+  /**
+   * Puts the template an the variables together.
+   *
+   * @return NULL|string|void
+   */
+  public function reunite()
+  {
+    return \Haanga::Load(
+      $this->template, $this->data->getArrayCopy()
+    );
+  }
+}

+ 92 - 0
php-pimf/pimf-framework/core/Pimf/View/Twig.php

@@ -0,0 +1,92 @@
+<?php
+/**
+ * View
+ *
+ * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
+ * @license   http://krsteski.de/new-bsd-license New BSD License
+ */
+
+namespace Pimf\View;
+
+use Pimf\Contracts\Reunitable;
+use Pimf\View;
+use Pimf\Registry;
+use Pimf\Util\Value;
+
+
+/**
+ * A view for TWIG a flexible, fast, and secure template engine for PHP.
+ *
+ * For use please add the following code to the end of the config.app.php file:
+ *
+ * <code>
+ *
+ * 'view' => array(
+ *
+ *   'twig' => array(
+ *     'cache'       => true,  // if compilation caching should be used
+ *     'debug'       => false, // if set to true, you can display the generated nodes
+ *     'auto_reload' => true,  // useful to recompile the template whenever the source code changes
+ *  ),
+ *
+ * ),
+ *
+ * </code>
+ *
+ * @link    http://twig.sensiolabs.org/documentation
+ * @package View
+ * @author  Gjero Krsteski <[email protected]>
+ * @codeCoverageIgnore
+ */
+class Twig extends View implements Reunitable
+{
+  /**
+   * @var \Twig_Environment
+   */
+  protected $twig;
+
+  /**
+   * @param string $template
+   * @param array  $data
+   */
+  public function __construct($template, array $data = array())
+  {
+    parent::__construct($template, $data);
+
+    $conf = Registry::get('conf');
+
+    require_once BASE_PATH . "Twig/lib/Twig/Autoloader.php";
+
+    \Twig_Autoloader::register();
+
+    $options = array('debug'       => Value::ensureBoolean($conf['view']['twig']['debug']),
+                     'auto_reload' => Value::ensureBoolean($conf['view']['twig']['auto_reload']),);
+
+    if ($conf['view']['twig']['cache'] === true) {
+      $options['cache'] = $this->path . '/twig_cache';
+    }
+
+    // define the Twig environment.
+    $this->twig = new \Twig_Environment(new \Twig_Loader_Filesystem(array($this->path)), $options);
+  }
+
+  /**
+   * @return \Twig_Environment
+   */
+  public function getTwig()
+  {
+    return $this->twig;
+  }
+
+  /**
+   * Puts the template an the variables together.
+   *
+   * @return string|void
+   */
+  public function reunite()
+  {
+    return $this->twig->render(
+      $this->template, $this->data->getArrayCopy()
+    );
+  }
+}

+ 6 - 0
php-pimf/pimf-framework/core/Pimf/_database/create-cache-table-mysql.sql

@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS `pimf_cache` (
+  `key` int(11) NOT NULL,
+  `value` longtext,
+  `expiration` INT(10) NOT NULL,
+  PRIMARY KEY (`key`)
+) ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=16;

+ 5 - 0
php-pimf/pimf-framework/core/Pimf/_database/create-cache-table-sqlite.sql

@@ -0,0 +1,5 @@
+CREATE TABLE  IF NOT EXISTS pimf_cache (
+     key VARCHAR PRIMARY KEY NOT NULL UNIQUE,
+     value TEXT NOT NULL,
+     expiration INTEGER NOT NULL
+);

+ 6 - 0
php-pimf/pimf-framework/core/Pimf/_database/create-session-table-mysql.sql

@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS `sessions` (
+     `id` VARCHAR(40) NOT NULL,
+     `last_activity` INT(10) NOT NULL,
+     `data` TEXT NOT NULL,
+     PRIMARY KEY (`id`)
+);

Some files were not shown because too many files changed in this diff