├── .htaccess ├── App ├── Config │ ├── database.php │ ├── gateway.php │ └── simpleDateTimeLocals.php ├── Controllers │ ├── Admin │ │ ├── DashboardController.php │ │ └── UserController.php │ ├── AuthController.php │ ├── Controller.php │ └── IndexController.php ├── Library │ ├── App.php │ ├── DatabaseObjectTrait.php │ ├── ErrorsBag.php │ ├── Exceptions │ │ ├── FileNotFoundException.php │ │ └── MethodNotFoundException.php │ ├── Gateway.php │ ├── MagicGetterTrait.php │ ├── Model.php │ ├── Redirect.php │ ├── Request.php │ ├── Response.php │ ├── Session.php │ ├── SimpleDateTime.php │ ├── UploadedFile.php │ ├── Url.php │ ├── Validator.php │ ├── View.php │ └── initializing.php ├── Model │ └── User.php └── View │ ├── admin │ ├── dashboard.php │ ├── includes │ │ ├── footer.php │ │ ├── nav.php │ │ └── notification.php │ └── user │ │ ├── create.php │ │ ├── delete.php │ │ ├── edit.php │ │ ├── includes │ │ └── table.php │ │ ├── index.php │ │ └── show.php │ ├── auth │ └── login.php │ ├── errors │ ├── 404.php │ └── error.php │ ├── home.php │ ├── includes │ └── nav.php │ └── layouts │ ├── admin_master.php │ └── master.php ├── LICENCE ├── README.md ├── assets ├── css │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ └── main.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── images │ ├── ajax-loader-32.gif │ └── product │ │ └── default.png └── js │ ├── app.js │ ├── bootstrap.min.js │ └── jquery-213.min.js ├── index.php └── simple-mvc_2016-10-01.sql /.htaccess: -------------------------------------------------------------------------------- 1 | Options -Indexes FollowSymLinks -Multiviews 2 | 3 | #php_value date.timezone "Africa/Cairo" 4 | #php_value date.default_latitude "30.05" 5 | #php_value date.default_longitude "31.25" 6 | 7 | RewriteEngine On 8 | 9 | # Ignore assets Directory 10 | RewriteCond %{REQUEST_URI} !(/assets) 11 | 12 | # Serve All Requests To index.php 13 | RewriteRule .* index.php [QSA,L] -------------------------------------------------------------------------------- /App/Config/database.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | */ 9 | 10 | 11 | return [ 12 | 'db_hostname' => 'simple-framework.dev', 13 | 'db_name' => 'simple-mvc', 14 | 'db_username' => 'root', 15 | 'db_password' => 'root', 16 | ]; -------------------------------------------------------------------------------- /App/Config/gateway.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | */ 9 | 10 | use App\Library\Redirect; 11 | 12 | return [ 13 | 'admin' => function(){ 14 | if( !$this->session->isLoggedIn() ) 15 | return Redirect::to( '/auth/login' ); 16 | 17 | return ( $this->session->user->admin ); 18 | }, 19 | 20 | 'auth/login' => function(){ 21 | return 1; 22 | }, 23 | 24 | ]; -------------------------------------------------------------------------------- /App/Config/simpleDateTimeLocals.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | */ 9 | 10 | return [ 11 | 12 | //English 13 | 'en' => [ 14 | 'day' => [ 15 | 'Sat' => 'Saturday', 16 | 'Sun' => 'Sunday', 17 | 'Mon' => 'Monday', 18 | 'Tue' => 'Tuesday', 19 | 'Wed' => 'Wednsday', 20 | 'Thu' => 'Thursday', 21 | 'Fri' => 'Friday' 22 | ], 23 | 'month' => [ 24 | 'Jan' => 'January', 25 | 'Feb' => 'February', 26 | 'Mar' => 'March', 27 | 'Apr' => 'April', 28 | 'May' => 'May', 29 | 'Jun' => 'June', 30 | 'Jul' => 'July', 31 | 'Aug' => 'August', 32 | 'Sep' => 'September', 33 | 'Oct' => 'October', 34 | 'Nov' => 'November', 35 | 'Dec' => 'December' 36 | ], 37 | 'period' => [ 38 | 'AM' => 'am', 39 | 'PM' => 'pm' 40 | ], 41 | 'rel'=>[ 42 | 'Yesterday' => 'Yestrday', 43 | 'Tomorrow' => 'Yestrday' 44 | ] 45 | ], 46 | 47 | // Arabic 48 | 'ar' => [ 49 | 'day' => [ 50 | 'Sat' => 'السبت', 51 | 'Sun' => 'الأحد', 52 | 'Mon' => 'الاثنين', 53 | 'Tue' => 'الثلاثاء', 54 | 'Wed' => 'الأربعاء', 55 | 'Thu' => 'الخميس', 56 | 'Fri' => 'الجمعه' 57 | ], 58 | 'month' => [ 59 | 'Jan' => 'يناير', 60 | 'Feb' => 'فبراير', 61 | 'Mar' => 'مارس', 62 | 'Apr' => 'أبريل', 63 | 'May' => 'مايو', 64 | 'Jun' => 'يونيو', 65 | 'Jul' => 'يوليو', 66 | 'Aug' => 'أغسطس', 67 | 'Sep' => 'سبتمبر', 68 | 'Oct' => 'أكتوبر', 69 | 'Nov' => 'نوفمبر', 70 | 'Dec' => 'ديسمبر' 71 | ], 72 | 'period'=> [ 73 | 'AM' => 'صباحا', 74 | 'PM' => 'مساءا' 75 | ], 76 | 'rel'=> [ 77 | 'Yesterday' => 'أمس', 78 | 'Tomorrow' => 'غدا' 79 | ] 80 | 81 | ], 82 | 83 | // Frensh 84 | 'fr' => [ 85 | 'day' => [ 86 | 'Sat' => 'Samedi', 87 | 'Sun' => 'Dimanche', 88 | 'Mon' => 'Lundi', 89 | 'Tue' => 'Mardi', 90 | 'Wed' => 'Mercredi', 91 | 'Thu' => 'Jeudi', 92 | 'Fri' => 'Vendredi' 93 | ], 94 | 'month' => [ 95 | 'Jan' => 'Janvier', 96 | 'Feb' => 'Fevrier', 97 | 'Mar' => 'Mars', 98 | 'Apr' => 'Avril', 99 | 'May' => 'Mai', 100 | 'Jun' => 'Juin', 101 | 'Jul' => 'Juillet', 102 | 'Aug' => 'Aout', 103 | 'Sep' => 'Septembre', 104 | 'Oct' => 'Octobre', 105 | 'Nov' => 'Novembere', 106 | 'Dec' => 'Decembere' 107 | ], 108 | 'period'=> [ 109 | 'AM' => 'am', 110 | 'PM' => 'pm' 111 | ], 112 | 'rel'=> [ 113 | 'Yesterday' => 'Yesterday', 114 | 'Tomorrow' => 'Tomorrow' 115 | ] 116 | ], 117 | 118 | // Other languages 119 | 120 | ]; -------------------------------------------------------------------------------- /App/Controllers/Admin/DashboardController.php: -------------------------------------------------------------------------------- 1 | 'username', 'order' => 'ASC' ]); 18 | // return User::all( [ 'order_by' => 'email', 'order' => 'DESC' ] ); 19 | 20 | return Response::view( 'admin.user.index', compact('users') ); 21 | } 22 | 23 | /** 24 | * Form Page To Insert New Record data 25 | * @return View Response Form Page 26 | */ 27 | public function create() 28 | { 29 | return Response::view( 'admin.user.create' ); 30 | } 31 | 32 | /** 33 | * Store New Record 34 | * @param Request $request App Request Instance 35 | * @return Redirect Redirect To New Created Record Page 36 | */ 37 | public function store( Request $request ) 38 | { 39 | $validator = Validator::make( $request->all(), [ 40 | 'username' => 'required|string|between:5,20|unique:users,username', 41 | 'email' => 'required|email|unique:users,email', 42 | 'password' => 'required|min:6', 43 | ], 44 | [ 45 | 'username.min' => 'Custom Error Message For Username Min Rule Error', 46 | // 'username.*' => 'Custom Error Message For Any Username Rule', 47 | // 'email.email' => 'Custom Error Message For Email Email Rule Error', 48 | ] ); 49 | 50 | if( $validator->fails() ) 51 | return Redirect::to( '/admin/user/create' ); 52 | 53 | 54 | $user = new User(); 55 | 56 | $user->username = $request->input( 'username' ); 57 | $user->email = $request->input( 'email' ); 58 | $user->password = $this->hashMake( $request->input( 'password' ) ); 59 | $user->admin = $request->has( 'admin' ); 60 | 61 | if( $user->save() ) 62 | { 63 | $this->makeSuccessNotification( 'User ' . $user->username . ' has successfully Created.' ); 64 | return Redirect::to( '/admin/user/' . $user->id ); 65 | } 66 | else 67 | { 68 | $this->makeErrorNotification( 'Error On Saving User ' . $user->username . ' Try Again Later.' ); 69 | return Redirect::to( '/admin/user' ); 70 | } 71 | 72 | } 73 | 74 | /** 75 | * Edit Record View 76 | * @param User $user Record To be Edit 77 | * @param Request $request App Request Instance 78 | * @return View Responce 79 | */ 80 | public function edit( User $user ) 81 | { 82 | return Response::view( 'admin.user.edit', compact('user') ); 83 | } 84 | 85 | /** 86 | * Display Single Record View 87 | * @param Record $user Record To Display 88 | * @return Response View 89 | */ 90 | public function show( User $user ) 91 | { 92 | return Response::view( 'admin.user.show', compact('user') ); 93 | } 94 | 95 | /** 96 | * Update Record 97 | * @param User $user User Instance 98 | * @param Request $request App Request 99 | * @return Redirect 100 | */ 101 | public function update( User $user, Request $request ) 102 | { 103 | // Validate 104 | $validator = Validator::make( $request->all(), [ 105 | 'username' => 'required|string|between:5,20|unique:users,username,'.$user->id, 106 | 'email' => 'required|email|unique:users,email,'.$user->id, 107 | 'password' => 'required|min:6', 108 | ] ); 109 | 110 | if( $validator->fails() ) 111 | return Redirect::to( '/admin/user/create' ); 112 | 113 | $user->username = $request->input( 'username' ); 114 | $user->email = $request->input( 'email' ); 115 | $user->password = $this->hashMake( $request->input( 'password' ) ); 116 | 117 | $user->admin = $request->has( 'admin' ); 118 | 119 | $user->updated_at = new SimpleDateTime(); 120 | 121 | if( $user->save() ) 122 | { 123 | $this->makeSuccessNotification( 'User ' . $user->username . ' has successfully Updated.' ); 124 | return Redirect::to( '/admin/user/' . $user->id ); 125 | } 126 | else 127 | { 128 | $this->makeErrorNotification( 'Error On Saving User ' . $user->username . ' Try Again Later.' ); 129 | return Redirect::to( '/admin/user/' . $user->id ); 130 | } 131 | } 132 | 133 | /** 134 | * Delete Single Record View 135 | * @param Record $user Record To Delete 136 | * @return Response View 137 | */ 138 | public function delete( User $user ) 139 | { 140 | return Response::view('admin.user.delete', compact('user')); 141 | } 142 | 143 | /** 144 | * Destroy Single Record 145 | * @param Record $user Record To Destroy 146 | * @return Redirect 147 | */ 148 | public function destroy( User $user ) 149 | { 150 | if( $user->id == $this->session->user->id ) 151 | { 152 | // User cannot Delete himself 153 | $this->makeErrorNotification( 'Sorry You cannot Delete Yourself!.' ); 154 | return Redirect::to( '/admin/user/' . $user->id ); 155 | } 156 | 157 | if( $user->delete() ) 158 | { 159 | $this->makeSuccessNotification( 'User ' . $user->username . ' has successfully Deleted.' ); 160 | return Redirect::to( '/admin/user' ); 161 | } 162 | else 163 | { 164 | $this->makeErrorNotification( 'User ' . $user->username . ' Couldnot be Deleted.' ); 165 | return Redirect::to( '/admin/user/' . $user->id ); 166 | } 167 | } 168 | 169 | 170 | } -------------------------------------------------------------------------------- /App/Controllers/AuthController.php: -------------------------------------------------------------------------------- 1 | session->isLoggedIn() ) 20 | return Redirect::to( '/' ); 21 | 22 | return Response::view( 'auth.login' ); 23 | } 24 | 25 | public function postLogin( Request $request ) 26 | { 27 | // return $request; 28 | $user = User::findWhere( [ 'email' => $request->input('email') ] ); 29 | 30 | if( $user && $this->hashVerify( $request->input('password'), $user->password ) ) 31 | { 32 | $this->session->login( $user ); 33 | $this->makeSuccessNotification( 'Welcome Back ' . $user->username . '.' ); 34 | return $user->admin ? Redirect::to( '/admin/' ) : Redirect::to( '/' ); 35 | } 36 | else 37 | { 38 | $this->makeErrorNotification( 'Login E-Mail Or Password Doesnot Match Any User Credential.' ); 39 | return Redirect::to( '/auth/login' ); 40 | } 41 | } 42 | 43 | public function logout() 44 | { 45 | if( !$this->session->isLoggedIn() ) 46 | return Redirect::to( '/' ); 47 | 48 | $this->session->logout(); 49 | return Redirect::to( '/' ); 50 | } 51 | } -------------------------------------------------------------------------------- /App/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | * 9 | * @property-read App\Library\Session $session Session Application session Instance 10 | * 11 | */ 12 | 13 | namespace App\Controllers; 14 | 15 | use App\Library\App; 16 | use App\Library\MagicGetterTrait; 17 | 18 | class Controller 19 | { 20 | use MagicGetterTrait; 21 | 22 | /** 23 | * Application session instance 24 | * @var App\Library\Session 25 | */ 26 | protected $session; 27 | 28 | 29 | /** 30 | * Constructor 31 | */ 32 | public function __construct() 33 | { 34 | // Register 35 | App::register( 'controller', $this ); 36 | 37 | $this->session = App::get( 'session' ); 38 | } 39 | 40 | /** 41 | * Make Session notification 42 | * @param String $type Notification Type: success, danger, warning 43 | * @param String $message Notification Message 44 | * @param String $link Notification Provided Link 45 | */ 46 | public function makeNotification( $type, $message, $link = null ) 47 | { 48 | $this->session->set( 'notification', compact( 'type', 'message', 'link' ) ); 49 | } 50 | 51 | 52 | /** 53 | * Make Session Succress notification 54 | * @param String $message Notification Message 55 | * @param String $link Notification Provided Link 56 | */ 57 | public function makeSuccessNotification( $message, $link = null ) 58 | { 59 | $type = 'success'; 60 | $this->session->set( 'notification', compact( 'type', 'message', 'link' ) ); 61 | } 62 | 63 | 64 | /** 65 | * Make Session Error notification 66 | * @param String $message Notification Message 67 | * @param String $link Notification Provided Link 68 | */ 69 | public function makeErrorNotification( $message, $link = null ) 70 | { 71 | $type = 'danger'; 72 | $this->session->set( 'notification', compact( 'type', 'message', 'link' ) ); 73 | } 74 | 75 | /** 76 | * Hash Password 77 | * @param String|Integer $value Password 78 | * @return String Hashed Password 79 | */ 80 | public function hashMake( $value ) 81 | { 82 | return password_hash( $value, PASSWORD_DEFAULT ); 83 | } 84 | 85 | /** 86 | * Verify Hashed Password 87 | * @param String|Integer $value Password 88 | * @param String $hash Hashed Password 89 | * @return Boolean True If Verified, Otherwise false 90 | */ 91 | public function hashVerify( $value, $hash ) 92 | { 93 | return password_verify( $value, $hash ); 94 | } 95 | 96 | } 97 | 98 | // الحمد لله 99 | // Ahmed saad, 1 Oct 2016 12:15 AM -------------------------------------------------------------------------------- /App/Controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | */ 11 | 12 | namespace App\Library; 13 | 14 | class App { 15 | 16 | /** 17 | * Array Holds Application Instances 18 | * @var array 19 | */ 20 | protected static $instances = []; 21 | 22 | /** 23 | * Register An Application Instance 24 | * @param String $key Instance Name 25 | * @param Object $instance Instance 26 | * @return Null 27 | */ 28 | public static function register( $key, $instance ) 29 | { 30 | if( !isset( self::$instances[ $key ] ) ) 31 | { 32 | // Register 33 | self::$instances[ $key ] = $instance; 34 | } 35 | } 36 | 37 | /** 38 | * Get An Registerd Instance 39 | * @param String $key Instance Name 40 | * @return Mixed Object If Found Otherwise Null 41 | */ 42 | public static function get( $key ) 43 | { 44 | return self::has( $key ) ? self::$instances[ $key ] : null; 45 | } 46 | 47 | 48 | /** 49 | * Check If An Instance Registered 50 | * @param String $key Instance name 51 | * @return boolean 52 | */ 53 | public static function has( $key ) 54 | { 55 | return isset( self::$instances[ $key ] ); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /App/Library/DatabaseObjectTrait.php: -------------------------------------------------------------------------------- 1 | 10 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 11 | */ 12 | 13 | namespace App\Library; 14 | 15 | use App\Library\App; 16 | use PDO; 17 | 18 | trait DatabaseObjectTrait 19 | { 20 | 21 | /** 22 | * PDO Instance 23 | * @var Object 24 | */ 25 | protected static $DBH; 26 | 27 | /** 28 | * Start Application Database Connection 29 | * @return Object PDO Instance 30 | */ 31 | public static function openConnection( ) 32 | { 33 | try 34 | { 35 | if( !isset( self::$DBH ) || !(self::$DBH instanceof PDO) ) 36 | { 37 | self::$DBH = new PDO( 'mysql:host=' . DB_HOSTNAME . ';dbname=' . DB_NAME, DB_USERNAME, DB_USERPWD ); 38 | 39 | self::$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); 40 | 41 | // Register in Service Container 42 | App::register( 'DBH', self::$DBH ); 43 | } 44 | 45 | return self::$DBH; 46 | 47 | } 48 | catch( PDOException $e ) 49 | { 50 | die( 'Database Error: ' . $e->getMessage() ); 51 | } 52 | # Method End 53 | } 54 | 55 | /** 56 | * Close Application Database Connection 57 | * @return Null 58 | */ 59 | public static function closeConnection() 60 | { 61 | if( isset( self::$DBH ) ) 62 | self::$DBH = null; 63 | 64 | # Method End 65 | } 66 | 67 | 68 | /** 69 | * Get Database PDO Instance 70 | * @return Object PDO Instance OR Null 71 | */ 72 | public static function getDBH() 73 | { 74 | if( !self::$DBH ) 75 | throw new Exception( 'there are no openned database connection' ); 76 | 77 | return self::$DBH; 78 | 79 | # Method End 80 | } 81 | 82 | 83 | ################################################################# 84 | 85 | 86 | /** 87 | * Count All Records For Current Model 88 | * @return Integer Records Count 89 | */ 90 | public static function countAll() 91 | { 92 | $sql = 'SELECT COUNT(*) FROM `' . static::$table_name . '`'; 93 | 94 | $result = self::findBySql( $sql, null, array( 'fetch_all' => false, 'fetch_mode' => PDO::FETCH_NUM ) ); 95 | 96 | return array_shift( $result ); 97 | 98 | # Method End 99 | } 100 | 101 | /** 102 | * Get All Model Records 103 | * @param array $options Query Options 104 | * @return Array || Object || False Query Result 105 | */ 106 | public static function all( Array $options = array() ) 107 | { 108 | #$fields = ( isset( $options['fields'] ) ) ? $options['fields'] : '*'; 109 | 110 | $sql = 'SELECT * FROM `' . static::$table_name . '`'; 111 | 112 | // self::completeQueryString( $sql, $options ); 113 | 114 | return self::findBySql( $sql, null, $options ); 115 | 116 | # Method End 117 | } 118 | 119 | /** 120 | * Get Model Record By Its Id 121 | * @param Integer $id record id 122 | * @return Array || Object || false query result 123 | */ 124 | public static function findById( $id, Array $options = array() ) 125 | { 126 | $options['fetch_all'] = false; 127 | // $options['fetch_mode'] = PDO::FETCH_CLASS; 128 | 129 | $sql = 'SELECT * FROM `' . static::$table_name . '` WHERE `id` = :id LIMIT 1'; 130 | 131 | return self::findBySql( $sql, array( 'id' => $id ), $options ); 132 | 133 | # Method End 134 | } 135 | 136 | /** 137 | * Get Model Records That Matches Supplied Conditions 138 | * @param Array $options Options 139 | * @return Array || Object || Boolean Query Result 140 | */ 141 | public static function findWhere( Array $field_value, Array $options = array() ) 142 | { 143 | $options = array_merge( 144 | array( 'fetch_all' => false ), 145 | $options 146 | ); 147 | 148 | $conditions = array(); 149 | 150 | foreach( $field_value as $field => $value ) 151 | $conditions[] = " `$field` = :$field"; 152 | 153 | 154 | $join_operator = ( isset( $options['operator'] ) && $options['operator'] == 'OR' ) ? ' OR' : ' AND'; 155 | 156 | unset( $options['operator'] ); 157 | 158 | $conditions = join( $conditions, $join_operator ); 159 | 160 | $sql = 'SELECT * FROM `' . static::$table_name . '` WHERE' . $conditions; 161 | 162 | // self::completeQueryString( $sql, $options ); 163 | 164 | return self::findBySql( $sql, $field_value, $options ); 165 | 166 | # Method End 167 | } 168 | 169 | /** 170 | * Get Model Records By Specific SQL Statement 171 | * @param string $sql full query string 172 | * @param array $prepare_values values to bind to query string 173 | * @param array $options Query options contains fetch_all, fetch_mode 174 | * @return Array || Object || Boolean return query result 175 | */ 176 | public static function findBySql( $sql, $prepare_values = null, Array $options = array() ) 177 | { 178 | $options = array_merge( 179 | array( 'fetch_mode' => PDO::FETCH_CLASS, 'fetch_all' => true ), 180 | $options 181 | ); 182 | 183 | self::completeQueryString( $sql, $options ); 184 | 185 | if( $prepare_values ) 186 | { 187 | # use prepare method 188 | $query = self::$DBH->prepare( $sql ); 189 | 190 | $query->execute( $prepare_values ); 191 | } 192 | else 193 | # use query method 194 | $query = self::$DBH->query( $sql ); 195 | 196 | 197 | # Get Result 198 | 199 | if( $options['fetch_mode'] == ( $options['fetch_mode'] | PDO::FETCH_CLASS ) ) 200 | { 201 | # Class Fetch 202 | $query->setFetchMode( $options['fetch_mode'], static::$class ); 203 | 204 | $fetch_class_mode = true; 205 | } 206 | else 207 | { 208 | $query->setFetchMode( $options['fetch_mode'] ); 209 | 210 | $fetch_class_mode = true; 211 | } 212 | 213 | 214 | $set = ( $options['fetch_all'] ) ? $query->fetchAll() : $query->fetch(); 215 | 216 | return ( $fetch_class_mode && $set ) ? self::checkCasting( $set ) : $set; 217 | 218 | # Method End 219 | } 220 | 221 | 222 | /** 223 | * Helper Method To Complete SQL Statement 224 | * @param string $sql query string 225 | * @param array $options query options 226 | */ 227 | private static function completeQueryString( &$sql, &$options ) 228 | { 229 | 230 | if( isset( $options['order_by'] ) ) 231 | { 232 | $sql .= ' ORDER BY ' . $options['order_by']; 233 | $sql .= ( isset( $options['order'] ) ) ? ' ' . $options['order'] : null; 234 | } 235 | 236 | 237 | $sql .= ( isset( $options['limit'] ) ) ? ' LIMIT ' . $options['limit'] : null; 238 | 239 | $sql .= ( isset( $options['offset'] ) ) ? ' OFFSET ' . $options['offset'] : null; 240 | 241 | unset( $options[ 'limit' ], $options[ 'offset' ], $options[ 'order_by' ] ); 242 | 243 | # Method End 244 | } 245 | 246 | 247 | ################################################################# 248 | 249 | /** 250 | * Choose the correct action method of create or update 251 | * @return Integer number of inserted or updated rows 252 | */ 253 | public function save() 254 | { 255 | return isset( $this->id ) ? $this->update() : $this->create(); 256 | } 257 | 258 | /** 259 | * Insert record 260 | * @return Integer number of inserted rows 261 | */ 262 | public function create() 263 | { 264 | $this->castForSave(); 265 | 266 | $field_value = array(); 267 | 268 | foreach( static::$db_fields as $field ) 269 | $field_value[ $field ] = $this->$field; 270 | 271 | 272 | $sql = 'INSERT INTO `' . static::$table_name . '` ( `' . join( static::$db_fields, '`, `' ) . '` ) VALUES ( :' . join( static::$db_fields, ', :' ) . ' )'; 273 | 274 | $query = self::$DBH->prepare( $sql ); 275 | 276 | $query->execute( $field_value ); 277 | 278 | $this->id = self::$DBH->lastInsertId(); 279 | 280 | return ( $query->errorCode() + 0 ) ? false : $query->rowCount() ; 281 | 282 | } 283 | 284 | /** 285 | * Update record 286 | * @return Integer number of updated rows, 0 mean no change happens 'may be updated but to the same values, so with no changes' 287 | */ 288 | public function update() 289 | { 290 | $this->castForSave(); 291 | 292 | $field_field = $field_value = array(); 293 | 294 | foreach( static::$db_fields as $field ) 295 | { 296 | $field_field[] = "`$field` = :$field"; 297 | 298 | $field_value[ $field ] = $this->$field; 299 | } 300 | 301 | 302 | $sql = 'UPDATE `' . static::$table_name . '` SET ' . join( $field_field, ' , ' ) . ' WHERE `id` = :id LIMIT 1'; 303 | 304 | $query = self::$DBH->prepare( $sql ); 305 | 306 | $query->execute( $field_value ); 307 | 308 | return ( $query->errorCode() + 0 ) ? false : $query->rowCount() ; 309 | 310 | # Method End 311 | } 312 | 313 | 314 | /** 315 | * Delete record 316 | * @return Integer number of deleted rows 317 | */ 318 | public function delete() 319 | { 320 | $sql = 'DELETE FROM `' . static::$table_name . '` WHERE `id` = :id'; 321 | 322 | $query = self::$DBH->prepare( $sql ); 323 | 324 | $query->execute( array( 'id' => $this->id ) ); 325 | 326 | return $query->rowCount(); 327 | } 328 | 329 | /** 330 | * Delete record 331 | * @return Integer number of deleted rows 332 | */ 333 | public static function checkCasting( $set ) 334 | { 335 | if( is_array( $set ) ) 336 | { 337 | foreach( $set as &$instance ) 338 | $instance->cast(); 339 | } 340 | else 341 | $set->cast(); 342 | 343 | return $set; 344 | } 345 | 346 | # Class End الحمد لله 347 | } 348 | 349 | ?> -------------------------------------------------------------------------------- /App/Library/ErrorsBag.php: -------------------------------------------------------------------------------- 1 | 10 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 11 | */ 12 | namespace App\Library; 13 | 14 | use Exception; 15 | use App\Library\App; 16 | 17 | class ErrorsBag 18 | { 19 | /** 20 | * Errors Array 21 | * @var Array 22 | */ 23 | protected $errors; 24 | 25 | /** 26 | * All Errors Count 27 | * @var Integer 28 | */ 29 | protected $count; 30 | 31 | /** 32 | * Constructor 33 | */ 34 | public function __construct() 35 | { 36 | $this->errors = []; 37 | $this->count = 0; 38 | } 39 | 40 | /** 41 | * Add An Error 42 | * @param string $key Error Key, EX: Input Name 43 | * @param Mixed $value Error Content, [ rule => value ] 44 | */ 45 | public function add( $key, Array $value ) 46 | { 47 | $this->errors[ $key ][ $value[0] ] = $value[1]; 48 | 49 | $this->count++; 50 | } 51 | 52 | 53 | /** 54 | * Check If There is Error For A Specifc Key 'INPUT' 55 | * @param String $key Input Name 56 | * @return boolean Input Has Any Error Or Not 57 | */ 58 | public function has( $key ) 59 | { 60 | return isset( $this->errors[ $key ] ); 61 | } 62 | 63 | /** 64 | * Check If There Are ny Error in Errors Array 65 | * @return Boolean Errors Array Has any Error Or Not 66 | */ 67 | public function any() 68 | { 69 | return !empty( $this->errors ); 70 | } 71 | 72 | /** 73 | * Get First Error For A Specific Input 74 | * @param String $key Input name 75 | * @return Mixed First Error Message For Input Or Null if there isnot 76 | */ 77 | public function first( $key ) 78 | { 79 | if( $this->has( $key ) ) 80 | { 81 | $first = array_slice( $this->errors[ $key ], 0, 1 ); 82 | return array_shift( $first ); 83 | } 84 | else 85 | return null; 86 | } 87 | 88 | /** 89 | * Get All Errors For Specific Input if Supplied, Otherwise Get All Errors In one Numeric Array 90 | * @param String $key Input Name | Optional 91 | * @return Array Numerical Based Array Of All Errors Or Empty Array If There Arenot 92 | */ 93 | public function all( $key = null ) 94 | { 95 | if( $key != null ) 96 | { 97 | if( $this->has( $key ) ) 98 | return $this->errors[ $key ]; 99 | else 100 | return []; 101 | } 102 | else 103 | { 104 | // Return all errors 105 | $all = []; 106 | foreach( $this->errors as $input => $erros ) 107 | { 108 | foreach( $errors as $rule => $error ) 109 | aray_push( $all, $input . ' : ' . $error ); 110 | } 111 | 112 | return $all; 113 | } 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /App/Library/Exceptions/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | */ 9 | 10 | namespace App\Library\Exceptions; 11 | 12 | use \Exception; 13 | 14 | class FileNotFoundException extends Exception 15 | { 16 | 17 | public function __construct( $filename = null, $code = null, Exception $previous = null ) 18 | { 19 | parent::__construct( 'File "' . $filename .'" Not Found.', $code, $previous ); 20 | } 21 | 22 | 23 | public function __toString() 24 | { 25 | return $this->getMessage(); 26 | } 27 | 28 | 29 | } -------------------------------------------------------------------------------- /App/Library/Exceptions/MethodNotFoundException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | */ 9 | 10 | namespace App\Library\Exceptions; 11 | 12 | use \Exception; 13 | 14 | class MethodNotFoundException extends Exception 15 | { 16 | 17 | public function __construct( $method = null, $code = null, Exception $previous = null ) 18 | { 19 | parent::__construct( $method . ' Method Not Found.', $code, $previous ); 20 | } 21 | 22 | 23 | public function __toString() 24 | { 25 | return $this->getMessage(); 26 | } 27 | 28 | 29 | } -------------------------------------------------------------------------------- /App/Library/Gateway.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read bool $passes If Condition Satisfies Request 12 | * @property-read Object $request App Request Instance 13 | * @property-read Object $session App Session Instance 14 | * @property-read Array $config App Defined Access Configurations 15 | */ 16 | 17 | namespace App\Library; 18 | 19 | use App\Library\Request; 20 | use App\Library\App; 21 | use App\Library\Redirect; 22 | use App\Library\MagicGetterTrait; 23 | 24 | 25 | class Gateway{ 26 | 27 | use MagicGetterTrait; 28 | 29 | /** 30 | * Represents Satisfaction Status 31 | * @var boolean 32 | */ 33 | protected $passes = true; 34 | 35 | /** 36 | * App Request Instance 37 | * @var Object 38 | */ 39 | protected $request; 40 | 41 | /** 42 | * Application Session Instance 43 | * @var Object 44 | */ 45 | protected $session; 46 | 47 | /** 48 | * Application Defined Access Configuration 49 | * @var Array 50 | */ 51 | protected $config; 52 | 53 | 54 | /** 55 | * Constructor 56 | * @param Request $request Application Request Instance 57 | */ 58 | public function __construct( Request $request ) 59 | { 60 | $this->request = $request; 61 | $this->session = App::get( 'session' ); 62 | App::register( 'gateway', $this ); 63 | 64 | $this->config = include CONF . DS . 'gateway.php'; 65 | 66 | return $this->check(); 67 | } 68 | 69 | /** 70 | * Check Defined Request Conditions 71 | * @return boolean If Passess Or Fails 72 | */ 73 | protected function check() 74 | { 75 | foreach( $this->config as $uri => $closure ) 76 | { 77 | if( $this->request->match( $uri ) ) 78 | { 79 | if( !call_user_func( $closure ) ) 80 | { 81 | $this->passes = false; 82 | break; 83 | } 84 | } 85 | } 86 | 87 | return $this->passes; 88 | } 89 | } -------------------------------------------------------------------------------- /App/Library/MagicGetterTrait.php: -------------------------------------------------------------------------------- 1 | 10 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 11 | * 12 | */ 13 | namespace App\Library; 14 | 15 | trait MagicGetterTrait 16 | { 17 | /** 18 | * Magic Getter 19 | * 20 | * Get Protected Properties As Readonly 21 | * @param string $property property name 22 | * @return mixed Property value 23 | */ 24 | public function __get($property) 25 | { 26 | if( property_exists( $this, $property ) ) 27 | return $this->$property; 28 | else 29 | return null; 30 | // else 31 | // throw new \RuntimeException( __CLASS__ . '::class accessing undefined property "' . $property . '"' ); 32 | } 33 | 34 | /** 35 | * Magic Getter 36 | * 37 | * Get Protected Properties As Readonly 38 | * @param string $property property name 39 | * @return mixed Property value 40 | */ 41 | public function __toString() 42 | { 43 | $arr = []; 44 | 45 | foreach( get_object_vars( $this ) as $p => $value ) 46 | { 47 | if( ( property_exists( $this, 'hidden' ) && in_array( $p, $this->hidden ) ) || in_array( $p, ['hidden', 'casts'] ) ) 48 | continue; 49 | 50 | $arr[ $p ] = $value; 51 | } 52 | 53 | // return $arr; 54 | return json_encode( $arr ); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /App/Library/Model.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read Array $hidden Model Hidden Attributes 12 | * @property-read Array $timestamps Model Timestamps Attributes 13 | */ 14 | 15 | namespace App\Library; 16 | 17 | use App\Library\DatabaseObjectTrait; 18 | use App\Library\MagicGetterTrait; 19 | use App\Library\SimpleDateTime; 20 | 21 | abstract class Model 22 | { 23 | use DatabaseObjectTrait, MagicGetterTrait; 24 | 25 | /** 26 | * Model Hidden Attributes 27 | * @var array 28 | */ 29 | protected $hidden = []; 30 | 31 | /** 32 | * Timestamps Attributes 33 | * @var Array 34 | */ 35 | protected $timestamps = [ 'created_at', 'updated_at' ]; 36 | 37 | /** 38 | * Cast Attributes To Defined Types To be Used In Model Instance 39 | * @return Null 40 | */ 41 | public function cast() 42 | { 43 | if( isset( $this->casts ) ) 44 | { 45 | foreach( $this->casts as $field => $type ) 46 | { 47 | if( $type == 'array' ) 48 | { 49 | $this->$field = json_decode( $this->$field, true ); 50 | 51 | if( !is_array( $this->$field ) ) 52 | $this->$field = isset( $this->$field ) ? [ $this->$field ] : []; 53 | } 54 | else if( $type == 'boolean' ) 55 | { 56 | $this->$field = $this->$field ? true : false; 57 | } 58 | else if( $type == 'integer' || $type == 'numeric' ) 59 | { 60 | $this->$field = $this->$field + 0; 61 | } 62 | else 63 | // $this->$field = settype( $this->$field, $type ); 64 | throw New Exception( 'Casting To Type ' . $type . ' Not supported.' ); 65 | } 66 | } 67 | 68 | // Time Stamps 69 | if( isset( $this->timestamps ) ) 70 | { 71 | foreach( $this->timestamps as $timestamp ) 72 | { 73 | if( property_exists( $this, $timestamp ) ) 74 | { 75 | if( $this->$timestamp ) 76 | $this->$timestamp = SimpleDateTime::parse( $this->$timestamp ); 77 | else 78 | { 79 | $this->$timestamp = new SimpleDateTime(); 80 | $this->save(); 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Cast Back Properties To Its Actual Type To Bes Inserted Ito Database 89 | * @return Null 90 | */ 91 | public function castForSave() 92 | { 93 | if( isset( $this->casts ) ) 94 | { 95 | foreach( $this->casts as $field => $type ) 96 | { 97 | if( property_exists( $this, $field ) ) 98 | { 99 | if( $type == 'array' ) 100 | { 101 | if( !is_array( $this->$field ) ) 102 | $this->$field = isset( $this->$field ) ? [ $this->$field ] : []; 103 | 104 | $this->$field = json_encode( $this->$field ); 105 | } 106 | else if( $type == 'boolean' ) 107 | $this->$field = $this->$field ? 1 : 0; 108 | 109 | else if( $type == 'integer' || $type == 'numeric' ) 110 | $this->$field = $this->$field + 0; 111 | 112 | else 113 | // $this->$field = settype( $this->$field, $type ); 114 | throw New Exception( 'Casting To Type ' . $type . ' Not supported.' ); 115 | } 116 | 117 | } 118 | } 119 | 120 | // Time Stamps 121 | if( isset( $this->timestamps ) ) 122 | { 123 | foreach( $this->timestamps as $timestamp ) 124 | { 125 | if( property_exists( $this, $timestamp ) ) 126 | { 127 | if( $this->$timestamp instanceof SimpleDateTime ) 128 | $this->$timestamp = $this->$timestamp->getMySQLDateTime(); 129 | 130 | else if( !empty( $this->$timestamp ) ) 131 | $this->$timestamp = SimpleDateTime::parse( $this->$timestamp )->getMySQLDateTime(); 132 | 133 | else 134 | $this->$timestamp = (new SimpleDateTime())->getMySQLDateTime(); 135 | } 136 | } 137 | } 138 | 139 | } 140 | 141 | } 142 | 143 | ?> -------------------------------------------------------------------------------- /App/Library/Redirect.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | */ 11 | namespace App\Library; 12 | 13 | class Redirect{ 14 | 15 | /** 16 | * Application Url Instance 17 | * @var Object 18 | */ 19 | protected static $url; 20 | 21 | /** 22 | * Redirect To Specific Location 23 | * @param String $url Path 24 | * @return Null 25 | */ 26 | public static function to( $url ) 27 | { 28 | header( 'Location: ' . static::$url->to( $url ) ); 29 | exit; 30 | } 31 | 32 | /** 33 | * set Application Url Instance 34 | * @param \App\Library\Url $url Url Instance 35 | */ 36 | public static function setUrlInstance( \App\Library\Url $url ) 37 | { 38 | static::$url = $url; 39 | } 40 | } -------------------------------------------------------------------------------- /App/Library/Request.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read String $request_uri Current Request Full URI 12 | * @property-read Array $request_segments Request Uri parts 13 | * @property-read Array $segments Request Uri parts execluding App prefixing keywords Like 'admin' 14 | * @property-read string $controller Controller name that will respond to request 15 | * @property-read string $method Controller method name that will respond to request 16 | * @property-read Array $parameters parameters that will passed to Controller method 17 | * @property-read Boolean $is_admin True If Requesting Admin Area 18 | * @property-read Boolean $is_ajax True If Request Was Made By Ajax 19 | * @property-read string $request_method Current Request Method get, post, put, patch, delete 20 | * @property-read string $root_url Application Root Url, If Application inside subdir in Document Root 21 | * @property-read string $request_result Result That Returned From Controller Method 22 | * @property-read string $inputs Request Input Array including post, get 23 | * @property-read string $files App\Library\UploadedFile Instance 24 | * 25 | */ 26 | 27 | namespace App\Library; 28 | 29 | use App\Library\MagicGetterTrait; 30 | use App\Library\Response; 31 | use App\Library\Url; 32 | use App\Library\App; 33 | use App\Library\UploadedFile; 34 | use App\Controllers\IndexController; 35 | use App\Library\Exceptions\FileNotFoundException; 36 | use App\Library\Exceptions\MethodNotFoundException; 37 | 38 | class Request 39 | { 40 | use MagicGetterTrait; 41 | 42 | /** 43 | * Request Full URI 44 | * @var null 45 | */ 46 | protected $request_uri; 47 | 48 | /** 49 | * Request Uri Parts 50 | * @var array 51 | */ 52 | protected $request_segments = []; 53 | 54 | /** 55 | * Request URI Parts Excluding prefixing keywords Like 'admin' 56 | * @var array 57 | */ 58 | protected $segments = []; 59 | 60 | /** 61 | * Controller name 62 | * @var string 63 | */ 64 | protected $controller; 65 | 66 | /** 67 | * Controller method 68 | * @var string 69 | */ 70 | protected $method; 71 | 72 | /** 73 | * Controller Method Parameters 74 | * @var array 75 | */ 76 | protected $parameters = []; 77 | 78 | /** 79 | * Is Requesting Admin Area 80 | * @var boolean 81 | */ 82 | protected $is_admin = false; 83 | 84 | /** 85 | * is Requested By Ajax 86 | * @var boolean 87 | */ 88 | protected $is_ajax = false; 89 | 90 | /** 91 | * Request Mathod 92 | * @var string 93 | */ 94 | protected $request_method = 'get'; 95 | 96 | /** 97 | * App Root Url 98 | * Useful if application hosted inside sub folder of document root 99 | * @var string 100 | */ 101 | protected $root_url; 102 | 103 | /** 104 | * Controller Method Returned Value 105 | * @var null 106 | */ 107 | protected $request_result; 108 | 109 | /** 110 | * Request Input Vars Post, Get vars 111 | * @var array 112 | */ 113 | protected $inputs = []; 114 | 115 | /** 116 | * File Instance 117 | * @var \App\library\UploadedFile 118 | */ 119 | protected $files; 120 | 121 | 122 | /** 123 | * Constructor 124 | */ 125 | public function __construct() 126 | { 127 | // Register in Service Container 128 | App::register( 'request', $this ); 129 | 130 | // Set Sub Path 131 | $this->root_url = str_replace( 'index.php', '', $_SERVER['SCRIPT_NAME'] ); 132 | 133 | if( isset( $_SERVER[ 'REDIRECT_URL' ] ) ) 134 | $this->request_uri = substr( $_SERVER[ 'REDIRECT_URL' ], strlen( $this->root_url ) ); 135 | else 136 | { 137 | // Remove query string if exists 138 | if( !empty( $_SERVER[ 'QUERY_STRING' ] ) ) 139 | $this->request_uri = substr( str_replace( '?'.$_SERVER[ 'QUERY_STRING' ], '', $_SERVER[ 'REDIRECT_URL' ] ), strlen( $this->root_url ) ); 140 | else 141 | $this->request_uri = substr( $_SERVER[ 'REDIRECT_URL' ], strlen( $this->root_url ) ); 142 | 143 | } 144 | 145 | // Instantiate Url Instance 146 | new Url( $this->root_url ); 147 | 148 | // Check Request Method 149 | if( $_SERVER[ 'REQUEST_METHOD' ] == 'GET' ) 150 | $this->request_method = 'get'; 151 | else 152 | { 153 | if( isset( $_POST[ 'method' ] ) ) 154 | { 155 | $this->request_method = strtolower( $_POST[ 'method' ] ); 156 | 157 | if( !in_array( $this->request_method, [ 'post', 'patch', 'put', 'update', 'delete' ] ) ) 158 | return Response::view( 'errors.error', [ 'maessage' => 'Method Not allowed', 'title' => 'Not Allowd', 'code' => 405 ], 405 )->send(); 159 | 160 | if( $this->request_method == 'patch' || $this->request_method == 'put' ) 161 | // Patch, Put Request 162 | $this->request_method = 'update'; 163 | } 164 | else 165 | $this->request_method = 'post'; 166 | 167 | } 168 | 169 | $this->setInputs(); 170 | 171 | // Check Ajax Request 172 | if( isset( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) && $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] == 'XMLHttpRequest' ) 173 | $this->is_ajax = true; 174 | 175 | $this->analyseRequest(); 176 | 177 | } 178 | 179 | /** 180 | * Analyse Request URI String 181 | * 182 | * Extract Controller Name, Method, Parameters 183 | * @return Null 184 | */ 185 | protected function analyseRequest() 186 | { 187 | $this->segments = $this->request_segments = $this->request_uri ? explode( '/', rtrim( $this->request_uri, '/' ) ) : []; 188 | 189 | if( !empty( $this->request_uri ) ) 190 | { 191 | // Check Admin 192 | if( $this->request_segments[0] == 'admin' ) 193 | { 194 | $this->is_admin = true; 195 | unset( $this->segments[0] ); 196 | $this->segments = array_values( $this->segments ); 197 | } 198 | 199 | } 200 | 201 | // MiddleWares 202 | $gateway = new \App\Library\Gateway( $this ); 203 | 204 | if( $gateway->passes ) 205 | { 206 | $this->setController(); 207 | 208 | // Load Controller 209 | $this->load(); 210 | } 211 | else 212 | { 213 | // echo 'Sorry error'; 214 | return Response::view( 'errors.error', [ 'message' => 'Un Authorized Access', 'title' => 'Sorry', 'code'=>'403' ], 403 )->send(); 215 | } 216 | 217 | } 218 | 219 | /** 220 | * Extract Controller Name, Method, Parameters 221 | */ 222 | protected function setController() 223 | { 224 | if( !empty( $this->segments ) ) 225 | { 226 | $this->controller = ucfirst( $this->checkSegmentName( $this->segments[0] ) ) . 'Controller'; 227 | 228 | // Check Controller 229 | if( $this->is_admin ) 230 | $controller_path = CONT . DS . 'Admin' . DS . $this->controller . '.php'; 231 | else 232 | $controller_path = CONT . DS . $this->controller . '.php'; 233 | 234 | 235 | if( !file_exists( $controller_path ) ) 236 | return Response::view( 'errors.404', [ 'code' => 404 ], 404 )->send(); 237 | 238 | 239 | 240 | $count = count( $this->segments ); 241 | 242 | if( $this->request_method == 'get' ) 243 | { 244 | // ID Not Nescssary except for show, 245 | if( $count == 1 ) 246 | // Index, No Parameters 247 | $this->method = 'index'; 248 | else if( $count == 2 ) 249 | { 250 | // Numeric 251 | if( is_numeric( $this->segments[1] ) ) 252 | { 253 | $this->method = 'show'; 254 | $this->parameters = [ $this->resolveResource( $this->segments[1] ), $this ]; 255 | return; 256 | } 257 | else 258 | { 259 | // Method 260 | // Check Known 261 | $this->method = $this->checkSegmentName( $this->segments[1] ); 262 | 263 | if( $this->method == 'edit' || $this->method == 'delete' ) 264 | return Response::view( 'errors.error', [ 'message' => 'Bad Request Method, Missing Resource ID', 'title' => 'Bad Request', 'code'=>'405' ], 405 )->send(); 265 | 266 | } 267 | } 268 | else 269 | { 270 | // Parameters 271 | // product/edit/1 272 | $this->method = $this->checkSegmentName( $this->segments[1] ); 273 | 274 | if( $this->method == 'edit' || $this->method == 'delete' ) 275 | { 276 | // Delete And Edit View Require Resource Instance 277 | if ( $count == 3 ) 278 | { 279 | $this->parameters = [ $this->resolveResource( $this->segments[2] ) ]; 280 | return; 281 | } 282 | else 283 | return Response::view( 'errors.error', [ 'message' => 'Bad Request Method', 'title' => 'Bad Request', 'code'=>'405' ], 405 )->send(); 284 | 285 | } 286 | else 287 | { 288 | if( $this->method == 'create' ) 289 | return Response::view( 'errors.error', [ 'message' => 'Bad Request Method', 'title' => 'Bad Request', 'code'=>'405' ], 405 )->send(); 290 | 291 | 292 | $this->parameters = array_slice( $this->segments, 2 ); 293 | return; 294 | } 295 | 296 | } 297 | } 298 | else // POST, PUT, PATCH, DELETE Request 299 | { 300 | // CUD 301 | // We Must Have Instance ID 302 | if( $count == 1 ) 303 | { 304 | if( $this->request_method == 'post' ) 305 | { 306 | // Store Request, Creating New Record 307 | $this->method = 'store'; 308 | $this->parameters = [ $this ]; 309 | return; 310 | } 311 | else 312 | // Only Post Request, Error 313 | return Response::view( 'errors.error', [ 'title'=>'Bad Request', 'message' => 'No Resources To Work On', 'code'=>405 ], 405 )->send(); 314 | 315 | } 316 | else if( $count == 2 ) 317 | { 318 | // Patch Update OR Delete 319 | if( $this->request_method == 'update' ) 320 | // Patch, Update Request 321 | $this->method = 'update'; 322 | 323 | else if( $this->request_method == 'delete' ) 324 | // Patch, Update Request 325 | $this->method = 'destroy'; 326 | 327 | else 328 | { 329 | // Custom Request 330 | $this->method = $this->request_method . $this->checkSegmentName( $this->segments[1] ); 331 | $this->parameters = [ $this ]; 332 | return; 333 | } 334 | 335 | $this->parameters = [ $this->resolveResource( $this->segments[1] ), $this ]; 336 | return; 337 | } 338 | else 339 | { 340 | if( $count == 3 && is_numeric( $this->segments[2] ) ) 341 | { 342 | // Custom CUD Methods 343 | // product/upload/1 344 | $this->method = $this->request_method . $this->checkSegmentName( $this->segments[1] ); 345 | $this->parameters = [ $this->resolveResource( $this->segments[2] ), $this ]; 346 | return; 347 | } 348 | 349 | // Error, CUD Doesnot Need More Than 3 Segments 350 | return Response::view( 'errors.error', [ 'title'=>'Bad Request', 'message' => 'Bad Request Method', 'code'=>405 ], 405 )->send(); 351 | } 352 | 353 | } 354 | } 355 | else 356 | { 357 | // FrontEnd Root OR Admin Root 358 | $this->controller = $this->is_admin ? 'DashboardController' : 'IndexController'; 359 | $this->method = 'index'; 360 | } 361 | } 362 | 363 | 364 | /** 365 | * Load Controller 366 | * @return Response App Response 367 | */ 368 | protected function load() 369 | { 370 | 371 | // Instantiate Controller 372 | $controller_name = $this->is_admin ? 'App\Controllers\Admin\\' . $this->controller : 'App\Controllers\\' . $this->controller; 373 | $controller = new $controller_name(); 374 | 375 | 376 | // Check Method 377 | if( !method_exists( $controller, $this->method ) ) 378 | return Response::view( 'errors.404', [ 'code' => 404 ], 404 )->send(); 379 | 380 | // Call Method 381 | $this->request_result = call_user_func_array( [ $controller, $this->method ], $this->parameters ); 382 | 383 | // Make Response From Controller Result, and send it to Browser; 384 | Response::make( $this->request_result )->send(); 385 | 386 | } 387 | 388 | 389 | /** 390 | * Helper Method, To Check And reformate Controller name 391 | * @param String $name Controller Name 392 | * @return String Reformed Controller name 393 | */ 394 | protected function checkSegmentName( $name ) 395 | { 396 | $name = strtolower( $name ); 397 | 398 | $name = preg_replace_callback( '/\-([a-zA-Z]{1})/i', function( $arr ){ 399 | return ucfirst( $arr[1] ); 400 | }, $name ); 401 | 402 | return $name; 403 | } 404 | 405 | 406 | /** 407 | * Convert id Parameter To Instance Of Model 408 | * @param Numeric $id Model record Id 409 | * @return Mixed Model Or Error Response if Not Found 410 | */ 411 | protected function resolveResource( $id ) 412 | { 413 | if( !is_numeric( $id ) ) 414 | return Response::view( 'errors.error', [ 'title'=>'Bad Request', 'message' => ' Bad Request', 'code' => 405 ], 405 )->send(); 415 | 416 | $model_name = str_replace( 'Controller', '', $this->controller ); 417 | $model_class = 'App\Model\\' . $model_name; 418 | 419 | if( !file_exists( MODEL . DS . $model_name .'.php' ) ) 420 | throw new FileNotFoundException( MODEL . DS . $model_name .'.php' ); 421 | 422 | if( $model = $model_class::findById( $id ) ) 423 | return $model; 424 | else 425 | return Response::view( 'errors.error', [ 'title'=>'Not Found', 'message' => $model_name . ' Not Found', 'code' => 404 ], 404 )->send(); 426 | 427 | } 428 | 429 | 430 | /** 431 | * Check If Current Request matches Given Uri 432 | * @param String $uri Uri To Match 433 | * @return Boolean True If matches 434 | */ 435 | public function match( $uri ) 436 | { 437 | $uri = explode( '/', trim( $uri, '/' ) ); 438 | 439 | if( ($c = count( $uri )) > count( $this->request_segments ) ) 440 | return false; 441 | 442 | $match = true; 443 | 444 | foreach( $uri as $i => $seg ) 445 | { 446 | if ( $seg != $this->request_segments[$i] ) 447 | { 448 | $match = false; 449 | break; 450 | } 451 | }; 452 | 453 | return $match; 454 | 455 | } 456 | 457 | 458 | 459 | /** 460 | * Set Request Inputs array 461 | */ 462 | protected function setInputs() 463 | { 464 | foreach( $_GET as $input=>$value ) 465 | $this->inputs[ $input ] = trim( $value ); 466 | 467 | foreach( $_POST as $input=>$value ) 468 | $this->inputs[ $input ] = trim( $value ); 469 | 470 | // if( !empty( $_FILES ) ) 471 | $this->files = UploadedFile::resolve(); 472 | } 473 | 474 | 475 | /** 476 | * Get Input value 477 | * @param String $key Input name 478 | * @param Mixed $default Default value 479 | * @return Mixed Input Value Otherwise Default 480 | */ 481 | public function input( $key, $default = null ) 482 | { 483 | // Get Input value 484 | if( $this->has( $key ) ) 485 | return $this->inputs[ $key ]; 486 | else 487 | return $default; 488 | } 489 | 490 | /** 491 | * Get All Request Inputs 492 | * @return Array All Request inputs 493 | */ 494 | public function all() 495 | { 496 | return $this->inputs; 497 | } 498 | 499 | /** 500 | * Check If Request has Specific Input 501 | * @param String $key Input Name 502 | * @return boolean true if exists 503 | */ 504 | public function has( $key ) 505 | { 506 | return isset( $this->inputs[ $key ] ); 507 | } 508 | 509 | } -------------------------------------------------------------------------------- /App/Library/Response.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read string $content_type response Content type 12 | * @property-read Integer $status_code response Status code 13 | * @property-read Array $ststus_text Status Code Corresponding Text 14 | * @property-read string $header_status response status header string 15 | * @property-read string $content response Content 16 | */ 17 | 18 | namespace App\Library; 19 | 20 | use App\Library\Request; 21 | use App\Library\View; 22 | use App\Library\App; 23 | 24 | 25 | class Response 26 | { 27 | /** 28 | * Server Protocol HTTP/1.1 OR HTTP/1.0 29 | * @var string 30 | */ 31 | protected static $server_protocol; 32 | 33 | /** 34 | * Response Content Type 35 | * @var string 36 | */ 37 | protected $content_type = 'text/html'; 38 | 39 | /** 40 | * Response Status Code 41 | * @var integer 42 | */ 43 | protected $status_code = 200; 44 | 45 | /** 46 | * Status code Corresponding text 47 | * @var Array 48 | */ 49 | protected $status_text = [ 50 | 200 => 'OK', 51 | 301 => 'Permanentaly Redirect', 52 | 301 => 'Permanentaly Redirect', 53 | 403 => 'Un Authorized', 54 | 404 => 'Not Found', 55 | 405 => 'Method Not allowed', 56 | ]; 57 | 58 | /** 59 | * Status Header String, HTTP/1.1 200 OK 60 | * @var String 61 | */ 62 | protected $header_status; 63 | 64 | /** 65 | * Response Content 66 | * @var string 67 | */ 68 | protected $content = ''; 69 | 70 | /** 71 | * Constructor 72 | * 73 | * @param Mixed $content Content To Make Response 74 | * @param integer $code Status Code 75 | * @param Array|null $headers Response Headers 76 | */ 77 | public function __construct( $content, $code = 200, Array $headers = null ) 78 | { 79 | // Register in Service Container 80 | App::register( 'response', $this ); 81 | 82 | // Controller May Instantiate Response, then send instance to Request as A Result 83 | static::$server_protocol = $_SERVER["SERVER_PROTOCOL"]; 84 | 85 | $this->content = $content; 86 | 87 | $this->status_code = $code; 88 | 89 | $this->header_status = static::$server_protocol . ' ' . $this->status_code . ' ' . @$this->status_text[ $this->status_code ] ; 90 | 91 | $this->setHeader( $this->header_status ); 92 | 93 | if( is_array( $headers ) ) 94 | { 95 | foreach( $headers as $key => $value ) 96 | $this->setHeader( $key, $value ); 97 | } 98 | 99 | } 100 | 101 | /** 102 | * Make Response From Raw text 103 | * 104 | * @param String $raw_string String To Make response 105 | * @param integer $code Status Code 106 | * @param Array|array $headers Headers 107 | * @return Response Response Instance 108 | */ 109 | public static function raw( $raw_string, $code = 200, Array $headers = [] ) 110 | { 111 | return new self( 112 | $raw_string, 113 | $code, 114 | array_merge( [ 'Content-Type' => 'text/html' ], $headers ) 115 | ); 116 | 117 | } 118 | 119 | /** 120 | * Make Json Response From Array 121 | * 122 | * @param Array|Object $array Array Or Object To make Response 123 | * @param integer $code Status Code 124 | * @param Array|array $headers Headers 125 | * @return Response Response Instance 126 | */ 127 | public static function json( $array, $code = 200, Array $headers = [] ) 128 | { 129 | self::checkArrayValuesForJson( $array ); 130 | 131 | return new self( 132 | is_array( $array ) ? json_encode( $array ) : $array, 133 | $code, 134 | array_merge( [ 'Content-Type' => 'application/json' ], $headers ) 135 | ); 136 | } 137 | 138 | /** 139 | * Make Json Response From View File 140 | * 141 | * @param String $view view file 142 | * @param Array $data View data 143 | * @param integer $code Status Code 144 | * @param Array|array $headers Headers 145 | * @return Response Response Instance 146 | */ 147 | public static function view( $view, $data = [], $code = 200, Array $headers = [] ) 148 | { 149 | // Make View 150 | $view = View::make( $view, $data ); 151 | 152 | return new self( 153 | $view->content, 154 | $code, 155 | array_merge( [ 'Content-Type' => 'text/html' ], $headers ) 156 | ); 157 | 158 | } 159 | 160 | /** 161 | * Make Response From Data 162 | * 163 | * THIS METHOD WILL DETECET DATA TYPE AND GUESS SUITABLE RESPONSE 164 | * 165 | * @param Mixed $array DATA TO MAKE RESPONSE From 166 | * @param integer $code Status Code 167 | * @param Array|array $headers Headers 168 | * @return Response Response Instance 169 | */ 170 | public static function make( $data, $code = 200, Array $headers = [] ) 171 | { 172 | if( $data instanceof self ) 173 | return $data; 174 | 175 | if( is_array( $data ) || is_object( $data ) ) 176 | // Json 177 | return self::json( $data, $code, $headers ); 178 | 179 | else 180 | // Plain Content 181 | return self::raw( $data, $code, $headers ); 182 | 183 | } 184 | 185 | /** 186 | * Set Header Value 187 | * 188 | * @param String $key Header Key 189 | * @param String|Null $value Key Value To Set 190 | */ 191 | protected function setHeader( $key, $value = null ) 192 | { 193 | $key = ( !$value ) ? $key : $key . ': ' . $value; 194 | 195 | ob_start(); 196 | @header( $key ); 197 | } 198 | 199 | /** 200 | * Helper To Check Array Values 201 | * 202 | * This Method Will Jsonify Array Values If was Objects 203 | * Note That: Those Objects Shoud Uses trait MagicGetterTrait 204 | * 205 | * @param [type] &$array [description] 206 | * @return [type] [description] 207 | */ 208 | protected static function checkArrayValuesForJson( &$array ) 209 | { 210 | if( is_array( $array ) ) 211 | { 212 | foreach( $array as &$element ) 213 | { 214 | if( is_object( $element ) ) 215 | $element = json_decode( (string) $element, true ); 216 | else if( is_array( $element ) ) 217 | self::checkArrayValuesForJson( $element ); 218 | } 219 | } 220 | } 221 | 222 | /** 223 | * Send Respanse To Client, Then Exit Execution 224 | * 225 | * @return Null 226 | */ 227 | public function send() 228 | { 229 | // ob_end_clean(); 230 | 231 | echo $this->content; 232 | 233 | flush(); 234 | 235 | exit; 236 | } 237 | } -------------------------------------------------------------------------------- /App/Library/Session.php: -------------------------------------------------------------------------------- 1 | 7 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 8 | * 9 | * @property-read App\Model\User $user User Instance 10 | */ 11 | namespace App\Library; 12 | 13 | use RuntimeException; 14 | use App\Library\App; 15 | use App\Model\User; 16 | use App\Library\MagicGetterTrait; 17 | 18 | class Session 19 | { 20 | use MagicGetterTrait; 21 | 22 | /** 23 | * Loged In User Instance 24 | * @var App\Model\User | Null 25 | */ 26 | protected $user = null; 27 | 28 | /** 29 | * Constructor 30 | */ 31 | public function __construct() 32 | { 33 | if( App::has( 'session' ) ) 34 | throw new RuntimeException( __METHOD__ . ' Canot Instantiate Another Session Instance, There Is Already Instantiated One, Call Session::getInstance()' ); 35 | 36 | @session_start(); 37 | 38 | // Clear Expired Flashed 39 | $this->clearFlashed(); 40 | 41 | // Login User 42 | if( $this->has( 'user_id' ) ) 43 | $this->loginUsingId( $this->get( 'user_id' ) ); 44 | 45 | // Register 46 | App::register( 'session', $this ); 47 | 48 | } 49 | 50 | /** 51 | * Save Value in Session 52 | * @param string $key Key To associate Value With 53 | * @param Mixed $value Value To save 54 | */ 55 | public function set( $key , $value ) 56 | { 57 | $_SESSION[ $key ] = $value; 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * Save Value in Session For Only One Request 64 | * After That It Will Be Cleared Automatically 65 | 66 | * @param string $key Value Key 67 | * @param Mixed $value Value To Save 68 | * @return Session Current Session Instance 69 | */ 70 | public function flash( $key , $value ) 71 | { 72 | $this->set( $key, $value ); 73 | 74 | if( $this->has( 's_flashing' ) ) 75 | array_push( $_SESSION[ 's_flashing' ], $key ); 76 | else 77 | $this->set( 's_flashing', [ $key ] ); 78 | 79 | // Check if existed in flashed, remove from 80 | if( $this->has( 's_flashed' ) && ($i = array_search( $key , $_SESSION['s_flashed'] )) !== false ) 81 | unset( $_SESSION['s_flashed'][$i] ); 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Clear Expired Flashed Values 88 | */ 89 | protected function clearFlashed() 90 | { 91 | // Delete Expired 92 | if( $this->has( 's_flashed' ) ) 93 | { 94 | foreach( $this->get('s_flashed') as $key ) 95 | { 96 | $this->forget( $key ); 97 | } 98 | 99 | $this->forget( 's_flashed' ); 100 | } 101 | 102 | // Move Current Flashed To Flashed 103 | if( $this->has( 's_flashing' ) ) 104 | { 105 | foreach( $this->get('s_flashing') as $i=>$key ) 106 | { 107 | unset( $_SESSION[ 'flashing' ][$i] ); 108 | 109 | if( $this->has( 's_flashed' ) ) 110 | array_push( $_SESSION[ 's_flashed' ], $key ); 111 | else 112 | $this->set( 's_flashed', [ $key ] ); 113 | } 114 | 115 | $this->forget( 's_flashing' ); 116 | } 117 | 118 | } 119 | 120 | /** 121 | * Check If Session has A Specific key 122 | * 123 | * * 'test' => [ 'subkey' => 'value' ] 124 | * $session->has( 'test' ) ==> true 125 | * $session->has( 'test', 'subkey' ) ==> true 126 | * $session->has( 'test', 'subkey2' ) ==> false 127 | * 128 | * @param string $key Key To Check If Stored Or Not 129 | * @param string $child_key Sub Key Of Previos key 130 | * @return boolean True if Exists Otherwise False 131 | */ 132 | public function has( $key, $child_key = null ) 133 | { 134 | if( $child_key ) 135 | return isset( $_SESSION[ $key ][ $child_key ] ); 136 | else 137 | return isset( $_SESSION[ $key ] ); 138 | } 139 | 140 | /** 141 | * Get Key Value Stored In Session 142 | * 143 | * * 'test' => [ 'subkey' => 'value' ] 144 | * $session->get( 'test' ) ==> [ 'subkey' => 'value' ] 145 | * $session->get( 'test', 'subkey' ) ==> 'value' 146 | * 147 | * @param string $key Value Key 148 | * @param string $default Sub Key 149 | * @return [type] [description] 150 | */ 151 | public function get( $key, $default = null ) 152 | { 153 | return $this->has( $key ) ? $_SESSION[ $key ] : $default; 154 | } 155 | 156 | /** 157 | * Get Session Value, Then Forget It 158 | * 159 | * @param string $key Value key 160 | * @param string $default Default Value 161 | * @return Mixed Value | Default 162 | */ 163 | public function pull( $key, $default = null ) 164 | { 165 | if( $val = $this->get( $key ) ) 166 | { 167 | $this->forget( $key ); 168 | 169 | return $val; 170 | } 171 | 172 | return $default; 173 | } 174 | 175 | /** 176 | * Delete Key From session 177 | * 178 | * @param String $key Key To Forget 179 | * @return Session Current Session instance 180 | */ 181 | public function forget( $key ) 182 | { 183 | if( $this->has( $key ) ) 184 | unset( $_SESSION[ $key ] ); 185 | 186 | return $this; 187 | } 188 | 189 | /** 190 | * Get Current Session Instance 191 | * 192 | * @return Session Instance 193 | */ 194 | public static function getInstance() 195 | { 196 | if( App::has( 'session' ) ) 197 | return App::get( 'session' ); 198 | else 199 | return new self(); 200 | } 201 | 202 | /** 203 | * LOGIN USER 204 | * 205 | * @param User $user User Instance 206 | * @return Session Current session Instance 207 | */ 208 | public function login( User $user ) 209 | { 210 | $this->set( 'user_id', $user->id ); 211 | $this->user = $user; 212 | 213 | return $this; 214 | # Method End 215 | } 216 | 217 | /** 218 | * Login User By Id 219 | * 220 | * @param Integer $id User Id 221 | * @return App\Model\User User Instance 222 | */ 223 | public function loginUsingId( $id ) 224 | { 225 | if( is_numeric( $id ) && $id > 0 ) 226 | { 227 | $this->user = User::findById( $id ); 228 | 229 | if( $this->user ) 230 | $this->set( 'user_id', $id ); 231 | else 232 | $this->user = null; 233 | 234 | return $this->user; 235 | } 236 | else 237 | throw new RuntimeException( __METHOD__ . ' Require One Parameter To Be Integer and Greater Than Zero' ); 238 | # Method End 239 | } 240 | 241 | /** 242 | * Check if There are Logged In User 243 | * 244 | * @return boolean True If There is Otherwise False 245 | */ 246 | public function isLoggedIn() 247 | { 248 | return $this->has( 'user_id' ) && $this->user; 249 | # Method End 250 | } 251 | 252 | 253 | /** 254 | * Logout User 255 | * 256 | * @return Session Current session Instance 257 | */ 258 | public function logout() 259 | { 260 | $this->forget( 'user_id' ); 261 | $this->user = null; 262 | 263 | return $this; 264 | # Method End 265 | } 266 | 267 | 268 | /** 269 | * Destroy Session 270 | */ 271 | public function destroy() 272 | { 273 | $this->logout(); 274 | unset( $_SESSION ); // note that this will delete any saved messages, notifications, etc 275 | session_destroy(); 276 | # Method End 277 | } 278 | 279 | } 280 | 281 | // الحمد لله 282 | // 25/9/2016 2:00 AM -------------------------------------------------------------------------------- /App/Library/SimpleDateTime.php: -------------------------------------------------------------------------------- 1 | 8 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 9 | * 10 | * @property-read Integer $_year Instance Year 11 | * @property-read Integer $_month Instance Month 12 | * @property-read Integer $_day Instance Day 13 | */ 14 | 15 | namespace App\Library; 16 | use App\Library\MagicGetterTrait; 17 | use \DateTime; 18 | use \Exception; 19 | 20 | 21 | class SimpleDateTime extends DateTime 22 | { 23 | use MagicGetterTrait; 24 | 25 | /** 26 | * Instance Year 27 | * @var Integer 28 | */ 29 | protected $_year; 30 | 31 | /** 32 | * Instance Month 33 | * @var Integer 34 | */ 35 | protected $_month; 36 | 37 | /** 38 | * Instance Day 39 | * @var Integer 40 | */ 41 | protected $_day; 42 | 43 | /** 44 | * Locals Translation Array 45 | * @var Array 46 | */ 47 | protected static $_translator = []; 48 | 49 | /** 50 | * Locle Language 51 | * @var string 52 | */ 53 | protected static $_local = 'en'; 54 | 55 | /** 56 | * Array Of Defined Locals 57 | * @var array 58 | */ 59 | protected static $_defined_locals = []; 60 | 61 | 62 | /** 63 | * Constructor 64 | * @param \DateTimeZone $TimeZone Time Zone Instance 65 | */ 66 | public function __construct( DateTimeZone $timezone = NULL ) 67 | { 68 | if( $timezone ) 69 | parent::__construct( 'now', $timezone ); 70 | 71 | else 72 | parent::__construct( 'now' ); 73 | 74 | $this->_year = ( int ) $this->format( 'Y' ); 75 | $this->_month = ( int ) $this->format( 'n' ); 76 | $this->_day = ( int ) $this->format( 'j' ); 77 | } 78 | 79 | 80 | /** 81 | * Set Time 82 | * @param Integer $houre Hour 83 | * @param Integer $minute Minute 84 | * @param integer $second Second 85 | * 86 | * @return SimpleDateTime urrent Instance 87 | * 88 | */ 89 | public function setTime( $houre, $minute, $second = 0 ) 90 | { 91 | if( !is_numeric( $houre ) || !is_numeric( $minute ) || !is_numeric( $second ) ) 92 | throw new Exception( "Date::setTime() excpects three numeric arguments the first two are requird and the third are optional, there are 'houre, minute, second' in the same order" ); 93 | 94 | if( $houre < 0 || $houre > 23 || $minute < 0 || $minute > 59 || $second < 0 || $second > 59 ) 95 | throw new Exception( 'invalid time' ); 96 | 97 | parent::setTime( $houre, $minute, $second ); 98 | 99 | return $this; 100 | 101 | } 102 | 103 | 104 | /** 105 | * Set Date 106 | * @param Integer $year Year 107 | * @param Integer $month Month 108 | * @param integer $day Day 109 | * 110 | * @return SimpleDateTime urrent Instance 111 | * 112 | */ 113 | public function setDate( $year, $month, $day ) 114 | { 115 | if( !is_numeric( $year ) || !is_numeric( $month ) || !is_numeric( $day ) ) 116 | throw new Exception( __METHOD__ . " excpects three numeric arguments all are requird , there are 'year, month, day' in the same order" ); 117 | 118 | if( !checkdate( $month, $day, $year ) ) 119 | throw new Exception( __METHOD__ . 'i nvalid date : none existing date' ); 120 | 121 | parent::setDate( $year, $month, $day ); 122 | $this->_year = ( int )$year; 123 | $this->_month = ( int )$month; 124 | $this->_day = ( int )$day; 125 | 126 | return $this; 127 | } 128 | 129 | 130 | /** 131 | * Disable Modify Method 132 | */ 133 | public function modify( $arg = '' ) 134 | { 135 | throw new Exception( __METHOD__ . ' has been disabled.' ); 136 | } 137 | 138 | 139 | /** 140 | * Set Date As DMY 141 | * @param String $dmy_date Date as d-m-Y, d/m/Y, d_m_Y, d:m:Y, d.m.Y 142 | * 143 | * @return Simple dateTime Current instance 144 | */ 145 | public function setDMY( $dmy_date ) 146 | { 147 | $date = preg_split( '/[-_ .:\/]{1,5}/', $dmy_date ); 148 | 149 | if( !is_array( $date ) || count( $date ) !=3 ) 150 | throw new Exception( __METHOD__ . 'invalid provided date format, require DD/MM/YYYY' ); 151 | 152 | $this->setDate( $date[2], $date[1], $date[0] ); 153 | 154 | return $this; 155 | } 156 | 157 | /** 158 | * Set Date As MDY 159 | * @param String $mdy_date Date as d-m-Y, d/m/Y, d_m_Y, d:m:Y, d.m.Y 160 | * 161 | * @return Simple dateTime Current instance 162 | */ 163 | public function setMDY( $mdy_date ) 164 | { 165 | $date = preg_split( '/[-_ .:\/]{1,5}/', $mdy_date ); 166 | 167 | if( !is_array( $date ) || count( $date ) !=3 ) 168 | throw new Exception( __METHOD__ . 'invalid provided date format, require MM/DD/YYYY' ); 169 | 170 | $this->setDate( $date[2], $date[0], $date[1] ); 171 | 172 | return $this; 173 | } 174 | 175 | 176 | 177 | /** 178 | * Set Date Time From MySql Date Format 179 | * 180 | * @param String $mysql_date Mysql DateFormat 'Y-m-d' 181 | * 182 | * @return SimpleDateTime 183 | */ 184 | public function setMySQLDate( $mysql_date ) 185 | { 186 | $date = preg_split( '/[-]{1}/', $mysql_date ); 187 | 188 | if( !is_array( $date ) || count( $date ) !=3 ) 189 | throw new Exception( __METHOD__ . 'invalid provided date format, Date::setMySQLDate() require YYYY-MM-DD' ); 190 | 191 | $this->setDate( $date[0], $date[1], $date[2] ); 192 | 193 | return $this; 194 | } 195 | 196 | 197 | /** 198 | * Set Date Time From MySql DateTime Format 199 | * 200 | * @param String $mysql_date Mysql DateTime Format 'Y-m-d H:i:s' 201 | * 202 | * @return SimpleDateTime 203 | */ 204 | public function setMySQLDateTime( $mysql_datetime ) 205 | { 206 | $date = preg_split( '/[-: ]{1}/', $mysql_datetime ); 207 | 208 | if( !is_array( $date ) || count( $date ) !=6 ) 209 | throw new Exception( __METHOD__ . 'invalid provided date format, Date::setMySQLDateTime() require YYYY-MM-DD H:i:s' ); 210 | 211 | $this->setDate( $date[0], $date[1], $date[2] ); 212 | $this->setTime( $date[3], $date[4], $date[5] ); 213 | 214 | return $this; 215 | } 216 | 217 | 218 | /** 219 | * Get Date In DMY Format 220 | * 221 | * @param String $seperator Seperator Between day, monthy, year 222 | * @return String date 223 | */ 224 | public function getDMY() 225 | { 226 | $args = $this->argumentAnalysis( func_get_args() ); 227 | 228 | $seperator = $args['seperator']; 229 | $zeroes = $args['zeroes']; 230 | 231 | $format = ( $zeroes ) ? 'd' . $seperator . 'm' . $seperator . 'Y' : 'j' . $seperator . 'n' . $seperator . 'Y'; 232 | 233 | return $this->format( $format ); 234 | } 235 | 236 | 237 | /** 238 | * Get Date In MDY Format 239 | * 240 | * @param String $seperator Seperator Between monthy, day, year 241 | * @return String date 242 | */ 243 | public function getMDY() 244 | { 245 | $args = $this->argumentAnalysis( func_get_args() ); 246 | 247 | $seperator = $args['seperator']; 248 | $zeroes = $args['zeroes']; 249 | 250 | $format = ( $zeroes ) ? 'm' . $seperator . 'd' . $seperator . 'Y' : 'n' . $seperator . 'j' . $seperator . 'Y'; 251 | 252 | return $this->format( $format ); 253 | } 254 | 255 | 256 | /** 257 | * Get Date In Mysql date Format 258 | * 259 | * @return String date 260 | */ 261 | public function getMySQLDate() 262 | { 263 | return $this->format( 'Y-m-d' ); 264 | } 265 | 266 | 267 | /** 268 | * Get Date In Mysql Date Time Format 269 | * 270 | * @return String date 271 | */ 272 | public function getMySQLDateTime() 273 | { 274 | return $this->format( 'Y-m-d H:i:s' ); 275 | } 276 | 277 | 278 | /** 279 | * Get day Number 280 | * 281 | * @param boolean $zeroes With Left Zero Like 01, 02, .., 09 or not 282 | * @return Integer Number 283 | */ 284 | public function getDay( $zeroes = false ) 285 | { 286 | return ( $zeroes ) ? $this->format( 'd' ) : $this->format( 'j' ); 287 | } 288 | 289 | 290 | /** 291 | * Get Numeric Day Ordinal Lile 1st, 2nd, 3rd 292 | * 293 | * @return String Ordinal day 294 | */ 295 | public function getDayOrdinal() 296 | { 297 | return $this->format( 'jS' ); 298 | } 299 | 300 | 301 | /** 302 | * Get Full Day Name 303 | * 304 | * @return String Full Day name, Friday, .... 305 | */ 306 | public function getDayName() 307 | { 308 | if( self::$_local != 'en' ) 309 | return self::translate( 'day', $this->getDayAbbr() ); 310 | 311 | else 312 | return $this->format( 'l' ); 313 | 314 | } 315 | 316 | 317 | /** 318 | * Get day Short name, Fri 319 | * 320 | * @return String Day Abbreviation 321 | */ 322 | public function getDayAbbr() 323 | { 324 | return $this->Format( 'D' ); 325 | } 326 | 327 | 328 | /** 329 | * Get Month Number 330 | * 331 | * @param boolean $zeroes With Left zero 01, ..., 09 332 | * @return Integer Month Number 333 | */ 334 | public function getMonth( $zeroes = false ) 335 | { 336 | return ( $zeroes ) ? $this->format( 'm' ) : $this->format( 'n' ); 337 | } 338 | 339 | 340 | /** 341 | * Get Month Full Name 342 | * 343 | * @return String Full Month name, April, .. 344 | */ 345 | public function getMonthName() 346 | { 347 | return ( self::$_local != 'en' ) ? self::translate( 'month', $this->getMonthAbbr() ) : $this->format( 'F' ); 348 | } 349 | 350 | 351 | /** 352 | * Get Month Short Name ( Abbreviation ) 353 | * 354 | * @return String Abbreviation Month name, Sep, .. 355 | */ 356 | public function getMonthAbbr() 357 | { 358 | return $this->format( 'M' ); 359 | } 360 | 361 | 362 | /** 363 | * Get Full Year 364 | * 365 | * @return Integer Full Year, 2012, .., 2016 366 | */ 367 | public function getFullYear() 368 | { 369 | return $this->format( 'Y' ); 370 | } 371 | 372 | 373 | /** 374 | * Get Year in Two Numeric Representation 375 | * 376 | * @return Integer Year, Like 12, ... , 16 377 | */ 378 | public function getYear() 379 | { 380 | return $this->format( 'y' ); 381 | } 382 | 383 | 384 | /** 385 | * Get Hour 386 | * @param integer $format 12 Or 24 Format 387 | * @return Integer Hour 388 | */ 389 | public function getHour( $format = 24 ) 390 | { 391 | return ( $format == 12 ) ? $this->format( 'g' ) : $this->format( 'H' ); 392 | } 393 | 394 | 395 | /** 396 | * Get Minute 397 | * 398 | * @return Integer Minuite 399 | */ 400 | public function getMinuite() 401 | { 402 | return $this->format( 'i' ); 403 | } 404 | 405 | /** 406 | * Get Second 407 | * 408 | * @return Integer Second 409 | */ 410 | public function getSecond() 411 | { 412 | return $this->format( 's' ); 413 | } 414 | 415 | 416 | /** 417 | * Get Period am | pm 418 | * 419 | * @return String am | pm 420 | */ 421 | public function getPeriod() 422 | { 423 | return ( self::$_local != 'en' ) ? self::translate( 'period', $this->format( 'A' ) ) : $this->format( 'A' ); 424 | } 425 | 426 | 427 | /** 428 | * Get Time 429 | * 9:30 AM 430 | * 431 | * @return String Time 432 | */ 433 | public function getTime() 434 | { 435 | return ( self::$_local != 'en' ) ? $this->format( 'g:i ' ) . $this->getPeriod( self::$_local ) : $this->format( 'g:i A' ); 436 | } 437 | 438 | /** 439 | * Get Time 440 | * 9:30:20 AM 441 | * 442 | * @return String Time 443 | */ 444 | public function getFullTime() 445 | { 446 | return ( self::$_local != 'en' ) ? $this->format( 'g:i:s' ) . ' ' . $this->getPeriod( self::$_local ) : $this->format( 'g:i:s A' ); 447 | } 448 | 449 | 450 | /** 451 | * Get Timezone Offset From GMT 452 | * 453 | * @return Integer Offset Hours 454 | */ 455 | public function getTimeZoneOffset() 456 | { 457 | return $this->getTimeZone()->getOffset( $this ) / ( 60 * 60 ); 458 | } 459 | 460 | 461 | /** 462 | * Add Days 463 | * 464 | * @param Integer $numDays Days To add 465 | * @return SimpleDateTime Instance 466 | */ 467 | public function addDays( $numDays ) 468 | { 469 | if( !is_numeric( $numDays ) || $numDays < 1 ) 470 | throw new Exception( __METHOD__ . ' expects positive integer' ); 471 | 472 | parent::modify( '+' . intval( $numDays ) . 'days' ); 473 | 474 | return $this; 475 | } 476 | 477 | 478 | /** 479 | * Sub Days 480 | * 481 | * @param Integer $numDays Days To Subtract 482 | * @return SimpleDateTime Instance 483 | */ 484 | public function subDays( $numDays ) 485 | { 486 | if( !is_numeric( $numDays ) ) 487 | throw new Exception( __METHOD__ . ' expects integer' ); 488 | 489 | parent::modify( '-' . abs( intval( $numDays ) ) . 'days' ); 490 | 491 | return $this; 492 | } 493 | 494 | 495 | /** 496 | * Add weeks 497 | * 498 | * @param Integer $numWeeks Weeks To add 499 | * @return SimpleDateTime instance 500 | */ 501 | public function addWeeks( $numWeeks ) 502 | { 503 | if( !is_numeric( $numWeeks ) || $numWeeks < 1 ) 504 | throw new Exception( __METHOD__ . ' expects positive integer' ); 505 | 506 | parent::modify( '+' . intval( $numWeeks ) . 'weeks' ); 507 | } 508 | 509 | 510 | /** 511 | * Subtract Weeks 512 | * 513 | * @param Integer $numWeeks Weeks To Subtract 514 | * @return SimpleDateTime instance 515 | */ 516 | public function subWeeks( $numWeeks ) 517 | { 518 | if( !is_numeric( $numWeeks ) ) 519 | throw new Exception( __METHOD__ . ' expects integer' ); 520 | 521 | parent::modify( '-' . abs( intval( $numWeeks ) ) . 'weeks' ); 522 | # re assighn _year, _month, _day values 523 | return $this; 524 | } 525 | 526 | 527 | /** 528 | * Add Months 529 | * 530 | * @param Integer $numMonths Months To add 531 | * @return SimpleDateTime instance 532 | */ 533 | public function addMonths( $numMonths ) 534 | { 535 | if( !is_numeric( $numMonths ) || $numMonths < 1 ) 536 | throw new Exception( __METHOD__ . ' expects positive integer' ); 537 | 538 | $newValue = $this->_month + intval( $numMonths ); 539 | 540 | if( $newValue <= 12 ) 541 | { 542 | // in the same year && $newValue is the new month 543 | $this->_month = $newValue; 544 | } 545 | else 546 | { 547 | // calculating the new month and the new year 548 | $notDecember = $newValue % 12; 549 | 550 | if( $notDecember ) 551 | { 552 | // the new month is $notDecember && the new year is the round down of the division of $newValue by 12 553 | $this->_month = $notDecember; 554 | $this->_year += floor( $newValue / 12 ); 555 | } 556 | else 557 | { 558 | // new month is december && the new year is as th above but subtracting 1 year 559 | $this->_month = 12; 560 | $this->_year = ( floor( $newValue / 12 ) ) - 1; 561 | } 562 | } 563 | 564 | $this->checkLastDayOfMonth(); 565 | 566 | $this->setDate( $this->_year, $this->_month, $this->_day ); 567 | 568 | return $this; 569 | 570 | } 571 | 572 | /** 573 | * Subtract Months 574 | * 575 | * @param Integer $numMonths Months To Subtract 576 | * @return SimpleDateTime instance 577 | */ 578 | public function subMonths( $numMonths ) 579 | { 580 | 581 | if( !is_numeric( $numMonths ) ) 582 | throw new Exception( __METHOD__ . ' expects integer' ); 583 | 584 | $newValue = $this->_month - ( abs( intval( $numMonths ) ) ); 585 | 586 | if( $newValue > 0 ) 587 | { 588 | // then we still in the same year and the month will be $newValue 589 | $this->_month = $newValue; 590 | } 591 | else 592 | { 593 | // we back to a prvious year 594 | $newValue = abs( $newValue ); 595 | $months = range( 12, 1 ); 596 | 597 | // calculating the month 598 | $monthPosition = $newValue % 12; 599 | $this->_month = $months[ $monthPosition ]; 600 | 601 | // calculating the year 602 | // this will depnds on if w are in decmber or not 603 | // in december $monthPosition will be 0 604 | if( $monthPosition ) 605 | // not decmber 606 | $this->_year -= ceil( $newValue / 12 ); 607 | 608 | else 609 | // december 610 | $this->_year -= ceil( $newValue / 12 ) + 1; 611 | 612 | } 613 | 614 | $this->CheckLastDayOfMonth(); 615 | $this->setDate( $this->_year, $this->_month, $this->_day ); 616 | 617 | return $this; 618 | } 619 | 620 | 621 | /** 622 | * Add Years 623 | * 624 | * @param Integer $numMonths Years To add 625 | * @return SimpleDateTime instance 626 | */ 627 | public function addYears( $numYears ) 628 | { 629 | if( !is_numeric( $numYears ) || $numYears < 1 ) 630 | throw new Exception( __METHOD__ . ' Expects positive integer' ); 631 | 632 | $this->_year += intval( $numYears ); 633 | $this->CheckLastDayOfMonth(); 634 | $this->setDate( $this->_year, $this->_month, $this->_day ); 635 | 636 | return $this; 637 | } 638 | 639 | 640 | /** 641 | * Subtract Years 642 | * 643 | * @param Integer $numMonths Years To Subtract 644 | * @return SimpleDateTime instance 645 | */ 646 | public function subYears( $numYears ) 647 | { 648 | if( !is_numeric( $numYears ) ) 649 | throw new Exception( __METHOD__ . ' Expects an integer' ); 650 | 651 | $this->_year -= abs( intval( $numYears ) ); 652 | $this->CheckLastDayOfMonth(); 653 | $this->setDate( $this->_year, $this->_month, $this->_day ); 654 | 655 | return $this; 656 | } 657 | 658 | 659 | /** 660 | * Get Date Difference Between Current instance And Given Date Object 661 | * @param Date $date date To Get Difference With 662 | * @return Array Difference As Array Of Days, Years, Months, Hours, Minuites, seconds 663 | */ 664 | public function getDateDiff( Date $date ) 665 | { 666 | 667 | # Assuming That Current Date instance '$this' is Older than the given Date Object 668 | 669 | # Get Relationship between two dates 670 | 671 | if( $this->getTimeStamp() >= $date->getTimeStamp() ) 672 | { 673 | # current instance is greater than the given date 674 | $greater =& $this; 675 | $smaller =& $date; 676 | 677 | $difference['current_to_given'] = 'after'; 678 | } 679 | else 680 | { 681 | $greater =& $date; 682 | $smaller =& $this; 683 | 684 | $difference['current_to_given'] = 'before'; 685 | } 686 | 687 | # Total Seconds Differences 688 | $difference['total_seconds'] = $greater->getTimeStamp() - $smaller->getTimeStamp(); 689 | 690 | 691 | # Seconds 692 | $diff = $greater->getSecond() - $smaller->getSecond(); 693 | 694 | if( $diff < 0 ) 695 | { 696 | $difference['seconds'] = 60 - abs( $diff ); 697 | $backwords_strip = 1; 698 | } 699 | else 700 | { 701 | $difference['seconds'] = abs( $diff ); 702 | $backwords_strip = 0; 703 | } 704 | 705 | # Minuites 706 | $diff = $greater->getMinuite() - $smaller->getMinuite()- $backwords_strip; 707 | 708 | if( $diff < 0 ) 709 | { 710 | $difference['minutes'] = 60 - abs( $diff ); 711 | $backwords_strip = 1; 712 | } 713 | else 714 | { 715 | $difference['minutes'] = abs( $diff ); 716 | $backwords_strip = 0; 717 | } 718 | 719 | # Hours 720 | $diff = $greater->getHour() - $smaller->getHour() - $backwords_strip; 721 | 722 | if( $diff < 0 ) 723 | { 724 | $difference['hours'] = 24 - abs( $diff ); 725 | $backwords_strip = 1; 726 | } 727 | else 728 | { 729 | $difference['hours'] = abs( $diff ); 730 | $backwords_strip = 0; 731 | } 732 | 733 | # Days 734 | $diff = $greater->getDay() - $smaller->getDay()- $backwords_strip; 735 | 736 | if( $diff < 0 ) 737 | { 738 | $difference['days'] = 30 - abs( $diff ); 739 | $backwords_strip = 1; 740 | } 741 | else 742 | { 743 | $difference['days'] = abs( $diff ); 744 | $backwords_strip = 0; 745 | } 746 | 747 | # Months 748 | $diff = $greater->getMonth() - $smaller->getMonth()- $backwords_strip; 749 | 750 | if( $diff < 0 ) 751 | { 752 | $difference['months'] = 12 - abs( $diff ); 753 | $backwords_strip = 1; 754 | } 755 | else 756 | { 757 | $difference['months'] = abs( $diff ); 758 | $backwords_strip = 0; 759 | } 760 | 761 | # Years 762 | $diff = $greater->getFullYear() - $smaller->getFullYear() - $backwords_strip; 763 | 764 | $difference['years'] = ( $diff ) ? abs( $diff ) : 0; 765 | 766 | return $difference; 767 | 768 | } 769 | 770 | 771 | /** 772 | * Get Human Readable Date Difference Relative To current Time (now)] 773 | * 774 | * EX: 2 days ago 775 | * 776 | * @return String Difference 777 | */ 778 | public function getHummanDiff() 779 | { 780 | 781 | # $diff = $this->getDateDiff( new self ); 782 | 783 | $current = new self; 784 | 785 | $diff = $current->getTimeStamp() - $this->getTimeStamp(); 786 | 787 | $relativity = ( $diff >= 0 ) ? 'ago' : 'later'; 788 | 789 | $diff = abs( $diff ); 790 | 791 | if( $diff < 11 ) 792 | { 793 | # 0 - 10 seconds 794 | return 'few seconds ' . $relativity; 795 | } 796 | else if( $diff < 60 ) 797 | { 798 | # 11 - 59 seconds 799 | return 'about ' . $diff . ' seconds ' . $relativity; 800 | } 801 | else if( ( $minutes = round( $diff / 60 ) ) < 60 ) 802 | { 803 | # 1 - 59 minutes 804 | if( $minutes == 1 ) 805 | return 'about a minute ' . $relativity; 806 | else 807 | return $minutes . ' minutes ' . $relativity; 808 | } 809 | else if( ( $hours = round( $diff / 3600 ) ) < 24 ) 810 | { 811 | # 1 - 23 hours 812 | if( $hours == 1 ) 813 | return 'about an hour ' . $relativity; 814 | else 815 | return $hours . ' hours ' . $relativity; 816 | } 817 | else if( round( $diff / 86400 ) < 7 ) 818 | { 819 | # 1 - 7 Days 820 | # Past Thursday at 5:45 pm 821 | $relativity = ( $relativity == 'ago' )? 'past' : 'next'; 822 | 823 | return $relativity . ' ' . $this->getDayName() . ' at ' . strtolower( $this->getTime() ) ; 824 | } 825 | else 826 | { 827 | # Month Day, Year at 3:10 pm 828 | return $this->getMonthName() . ' ' . $this->getDay() . ', ' . $this->getFullYear() . ' at ' . strtolower( $this->getTime() ); 829 | } 830 | } 831 | 832 | 833 | /** 834 | * Check If Year Is Leap Year 835 | * 836 | * @return boolean True If Leap Year Other wise False 837 | */ 838 | public function isLeap() 839 | { 840 | // in leap year february has 29 days 841 | // leap year is divisible by 400 or ( devisible by 4 && not divisible by 100 ) 842 | if( $this->_year % 400 == 0 || ( $this->_year % 4 == 0 && $this->_year %100 != 0 ) ) 843 | return true; 844 | 845 | else 846 | return false; 847 | } 848 | 849 | 850 | /** 851 | * Check Last day Of Month If Valid day Or Not 852 | * 853 | * @return Integer Correct last day 854 | */ 855 | final protected function checkLastDayOfMonth() 856 | { 857 | // if the date doesn't exists that's will be due to last day of the month 858 | if( !checkdate( $this->_month, $this->_day, $this->_year ) ) 859 | { 860 | $use30 = array( 4, 6, 9, 11 ); 861 | 862 | if( in_array( $this->_month, $use30 ) ) 863 | 864 | // these months alwayes uses 30 days 865 | $this->_day = 30; 866 | 867 | else 868 | 869 | // month must be february so last day will be 28 but in case of leap years it will be 29 870 | $this->_day = $this->isLeap() ? 29 : 28; 871 | 872 | } 873 | } 874 | 875 | 876 | /** 877 | * Instantiate Instance And Set It From Mysql dateTime Formate 878 | * 879 | * @param String $mysql_datetime Mysql dateTime Format 880 | * 881 | * @return SimpleDateTime Instance 882 | */ 883 | public static function setFromMySQLDateTime( $mysql_datetime ) 884 | { 885 | // note that setMySQLDateTime() method will do the checking on $mysql_datetim 886 | $time = new self; 887 | 888 | $time->setMySQLDateTime( $mysql_datetime ); 889 | 890 | return $time; 891 | } 892 | 893 | /** 894 | * Instantiate And Set Instance From Date String 895 | * 896 | * @param String $string Any valis string That Works with strtotime() 897 | * @return SimpledateTime Instance 898 | */ 899 | public static function parse( $string ) 900 | { 901 | $timestamp = strtotime( $string ); 902 | 903 | return self::setFromTimeStamp( $timestamp ); 904 | } 905 | 906 | /** 907 | * Instantiate instance And Set From Timestamp 908 | * 909 | * @param Integer $time_stamp Timestamp 910 | * 911 | * @return SimpledateTime Instance 912 | */ 913 | public static function setFromTimeStamp( $time_stamp ) 914 | { 915 | 916 | if( !is_numeric( $time_stamp ) ) 917 | throw new Exception( __METHOD__ . ' method needs one argumnet represents unix timestamp.' ); 918 | 919 | $time = new self; 920 | 921 | $time->setTimeStamp( $time_stamp ); 922 | 923 | return $time; 924 | } 925 | 926 | /** 927 | * Set dateTime Local language 928 | * 929 | * @param string $value Local name 930 | */ 931 | public static function setLocal( $value = 'en' ) 932 | { 933 | if( empty( self::$_translator ) ) 934 | self::prepareTranslator(); 935 | 936 | if( in_array( self::$_defined_locals, $value ) ) 937 | static::$_local = $value; 938 | } 939 | 940 | /** 941 | * Set default Php TimeZone 942 | * @param String $timezone Timezone String 943 | */ 944 | public static function setDefaultTimeZone( $timezone ) 945 | { 946 | ini_set( 'date.timezone', $timezone ); 947 | } 948 | 949 | 950 | /** 951 | * Magic Methods 952 | * 953 | * @return string String Representration Of Instance 954 | */ 955 | public function __toString() 956 | { 957 | if( self::$_local == 'ar' ) 958 | return $this->getDay() . ' ' . $this->getMonthName() . ' ' . $this->getFullYear() . ' ' . $this->getTime(); 959 | else 960 | return $this->getMonthName() . ' ' . $this->getDay() . ', ' . $this->getFullYear() . ' ' . $this->getTime(); 961 | } 962 | 963 | 964 | /** 965 | * Helper : 966 | * Analayze Arguments, To Set Zeroes And Seperator 967 | * 968 | * @param Array $args Arguments 969 | * @return Array Array Containing Zeroes and Seperator 970 | */ 971 | private function argumentAnalysis( $args ) 972 | { 973 | 974 | if( count( $args ) > 2 ) 975 | throw new Exception( "Date::getDMY(), Date::getMDY(), Date::getMySQLDate(), Date::getMySQLDateTime() don't requires more than two arguments the seprator, zeroes." ); 976 | 977 | // default values 978 | $seperator = '/'; 979 | $zeroes = false; 980 | 981 | $setted_sprator = false; 982 | $setted_zeroes = false; 983 | 984 | foreach( $args as $arg ) 985 | { 986 | if( preg_match( '/^( [-_.: \/]{1,3} )$/', $arg ) ) 987 | { 988 | if( $setted_sprator ) 989 | { 990 | continue; // go to next $arg 991 | } 992 | else 993 | { 994 | $seperator = $arg; 995 | $setted_sprator = true; 996 | continue; 997 | } 998 | } 999 | 1000 | if( $arg === true || $arg === false ) 1001 | { 1002 | if( $setted_zeroes ) 1003 | { 1004 | continue; 1005 | } 1006 | else 1007 | { 1008 | $zeroes = $arg; 1009 | $setted_zeroes = true; 1010 | } 1011 | } 1012 | } 1013 | 1014 | return array( 1015 | 'seperator' => $seperator, 1016 | 'zeroes' => $zeroes 1017 | ); 1018 | 1019 | } 1020 | 1021 | 1022 | /** 1023 | * Helper: Translate Date Tranlatable Part 1024 | * 1025 | * @param String $type Date Type One of month, day, period, rel 1026 | * @param String $date_part Part To Translate 1027 | * @return String Translated String Or Original If not Fount In Traslation array 1028 | */ 1029 | private function translate( $type, $date_part ) 1030 | { 1031 | // 1- Prepare languages array 1032 | if( empty( self::$_translator ) ) 1033 | self::prepareTranslator(); 1034 | 1035 | return isset( self::$_translator[ self::$_local ][ $type ][ $date_part ] ) ? self::$_translator[ self::$_local ][ $type ][ $date_part ] : $date_part; 1036 | } 1037 | 1038 | /** 1039 | * Helper: 1040 | * Prepare Translation Array For Translator 1041 | */ 1042 | private static function prepareTranslator() 1043 | { 1044 | // defining corresponding languages values 1045 | self::$_translator = require_once( CONF . DS . 'simpleDateTimeLocals.php' ); 1046 | self::$_defined_locals = array_keys( self::$_translator ); 1047 | } 1048 | 1049 | } 1050 | 1051 | // الحمد لله 1052 | // Updated 30 Sep 10:25 PM -------------------------------------------------------------------------------- /App/Library/UploadedFile.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read String $name File name 12 | * @property-read String $type File Mime-name 13 | * @property-read Numeric $size File Size 14 | * @property-read String $tmp_name File Temporary Uploaded Full path 15 | * @property-read String $tmp_name File Temporary Uploaded Full path 16 | * @property-read Integer $error File Upload Error Code 17 | * @property-read Boolean $valid if Valid Uploaded File ( Has No errors ) 18 | */ 19 | 20 | namespace App\Library; 21 | 22 | use App\Library\MagicGetterTraiat; 23 | 24 | class UploadedFile 25 | { 26 | use MagicGetterTrait; 27 | 28 | 29 | /** 30 | * All Files Instances 31 | * @var array 32 | */ 33 | protected static $instances = []; 34 | 35 | /** 36 | * All Files Uploading Errors 37 | * @var array 38 | */ 39 | protected static $errors = []; 40 | 41 | /** 42 | * Upload Errors Corresponding Text 43 | * @var Array 44 | */ 45 | protected static $_error_message = [ 46 | UPLOAD_ERR_OK => 'No Errors', 47 | UPLOAD_ERR_INI_SIZE => 'File is larger than php max file size', 48 | UPLOAD_ERR_FORM_SIZE => 'File is larger than form UPLOAD_MAX_SIZE', 49 | UPLOAD_ERR_PARTIAL => "Upload didn't compleated 'uploaded partially'", 50 | UPLOAD_ERR_NO_FILE => 'No files to upload', 51 | UPLOAD_ERR_NO_TMP_DIR => 'No defined temporary directory', 52 | UPLOAD_ERR_CANT_WRITE => "Don't have permission to write on disk", 53 | UPLOAD_ERR_EXTENSION => "Couldn't upload due to file extension problem" 54 | ]; 55 | 56 | /** 57 | * All Uploaded Files Names 58 | * @var array 59 | */ 60 | protected static $uploaded_files_names = []; 61 | 62 | /** 63 | * Uploaded File name 64 | * @var String 65 | */ 66 | protected $name; 67 | 68 | /** 69 | * Uploaded File Mime-Type 70 | * @var String 71 | */ 72 | protected $type; 73 | 74 | /** 75 | * Uploaded Filesize 76 | * @var Numeric 77 | */ 78 | protected $size; 79 | 80 | /** 81 | * Uploaded File Temporary Path 82 | * @var String 83 | */ 84 | protected $tmp_name; 85 | 86 | /** 87 | * Uploaded File Errors 88 | * @var Integer 89 | */ 90 | protected $error; 91 | 92 | /** 93 | * True If Is Valid uploaded File, Uplaoded Without errors 94 | * @var Boolean 95 | */ 96 | protected $valid; 97 | 98 | 99 | /** 100 | * Constructor 101 | * 102 | * @param Array $file Uploaded File Info array 103 | * @param String $input File Input name 104 | */ 105 | public function __construct( Array $file = null, $input = null ) 106 | { 107 | 108 | if( $input ) 109 | { 110 | $this->name = $file[ 'name' ]; 111 | $this->type = $file[ 'type' ]; 112 | $this->tmp_name = $file[ 'tmp_name' ]; 113 | $this->size = $file[ 'size' ]; 114 | $this->error = $file[ 'error' ]; 115 | 116 | if( !empty( static::$instances[ $input ] ) ) 117 | array_push( static::$instances[ $input ], $this ); 118 | 119 | else 120 | static::$instances[ $input ] = [ $this ]; 121 | 122 | // Set Validity Status 123 | $this->valid = $this->isValid(); 124 | } 125 | 126 | } 127 | 128 | /** 129 | * Resolve $_FILES Super Global And Instantiate Instance For each File 130 | * 131 | * @return UploadedFile Instance 132 | */ 133 | public static function resolve() 134 | { 135 | if( empty( $_FILES ) ) 136 | return new self(); 137 | 138 | foreach( $_FILES as $input_name => $file ) 139 | { 140 | if( !is_array( $file['name'] ) ) 141 | { 142 | new self( $file, $input_name ); 143 | } 144 | 145 | else 146 | { 147 | foreach( $file['name'] as $i => $value ) 148 | { 149 | $f = []; 150 | $f[ 'name' ] = $file[ 'name' ][ $i ]; 151 | $f[ 'type' ] = $file[ 'type' ][ $i ]; 152 | $f[ 'size' ] = $file[ 'size' ][ $i ]; 153 | $f[ 'tmp_name' ] = $file[ 'tmp_name' ][ $i ]; 154 | $f[ 'error' ] = $file[ 'error' ][ $i ]; 155 | 156 | new self( $f, $input_name ); 157 | } 158 | } 159 | } 160 | 161 | return new self(); 162 | } 163 | 164 | /** 165 | * Get Array Of All Uplaoded Files Instances 166 | * 167 | * @return Array Array Of All instances 168 | */ 169 | public function all() 170 | { 171 | $arr = []; 172 | 173 | foreach( static::$instances as $input => $instances ) 174 | { 175 | $set = $this->get( $input ); 176 | 177 | if( !empty( $set ) ) 178 | $arr = array_merge( $arr, $set ); 179 | } 180 | 181 | return $arr; 182 | } 183 | 184 | /** 185 | * Get Instances For aspecific Input 186 | * 187 | * @param String $input Input name 188 | * @return Array Array Of instances 189 | */ 190 | public function get( $input ) 191 | { 192 | return ( $this->has( $input ) ) ? self::$instances[ $input ] : null; 193 | } 194 | 195 | 196 | /** 197 | * Check if There Are Files For A Specific Input name 198 | * 199 | * @param String $input Input name 200 | * @return boolean true If There Are files Otherwise False 201 | */ 202 | public function has( $input ) 203 | { 204 | return isset( static::$instances[ $input ] ); 205 | } 206 | 207 | 208 | /** 209 | * Check If Instance Is Valid Uploaded file 210 | * 211 | * @return boolean True If Valid Otherwise False 212 | */ 213 | public function isValid() 214 | { 215 | if( $this->error != 0 ) 216 | { 217 | array_push( static::$errors, $this->name . ' ' . self::$_error_message[ $this->error ] ); 218 | return false; 219 | } 220 | else 221 | return true; 222 | } 223 | 224 | 225 | /** 226 | * Save Uploaded File To Specific Path With name 227 | * 228 | * @param string $full_path Save Full Path Including Flename 229 | * @return Boolean True On success False On fail 230 | */ 231 | public function saveTo( $full_path ) 232 | { 233 | if( $this->valid ) 234 | { 235 | if( move_uploaded_file( $this->tmp_name, $full_path ) ) 236 | { 237 | // Success 238 | array_push( self::$uploaded_files_names, $this->name ); 239 | 240 | return true; 241 | } 242 | else 243 | { 244 | array_push( static::$errors, 'Couldnot Move Uploaded File, Check save Destination Permissions.' ); 245 | 246 | return false; 247 | } 248 | } 249 | 250 | return false; 251 | } 252 | 253 | 254 | /** 255 | * Check If There Are Errors For Any Of Upload Files 256 | * 257 | * @return boolean True If has errors Otherwise false 258 | */ 259 | public function hasErrors() 260 | { 261 | return !empty( static::$errors ); 262 | } 263 | 264 | 265 | /** 266 | * Get errors Array 267 | * 268 | * @return Array Errors Array 269 | */ 270 | public function getErrors() 271 | { 272 | return static::$errors; 273 | } 274 | 275 | /** 276 | * Get Array of All Uplaoded Files Names 277 | * 278 | * @return Array Uplaoded Files name array 279 | */ 280 | public function getUploadedNames() 281 | { 282 | return static::$uploaded_files_names; 283 | } 284 | 285 | 286 | /** 287 | * Set File Save name 288 | * 289 | * @param String|Function $name Callback to Set name Or String Represents name 290 | * 291 | * @return UploadedFile Instance 292 | */ 293 | public function setSaveName( $name ) 294 | { 295 | if( is_callable( $name ) ) 296 | { 297 | $this->name = call_user_func( $name, $this->name ); 298 | } 299 | else 300 | $this->name = $name; 301 | 302 | return $this; 303 | } 304 | 305 | } 306 | 307 | // الحمد لله 308 | // Ahmed Saad, Updated at Fri 30, Sep 2016 -------------------------------------------------------------------------------- /App/Library/Url.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read String $root_uri App Root rel Path 12 | * @property-read Boolean $must_check If Url Must Check given path ( it will Be true if there are Root_Rel path ) 13 | * 14 | */ 15 | 16 | namespace App\Library; 17 | use App\Library\App; 18 | use App\Library\Redirect; 19 | use App\Library\MagicGetterTrait; 20 | 21 | class Url{ 22 | 23 | use MagicGetterTrait; 24 | 25 | /** 26 | * Application Root Relative Path 27 | * @var String 28 | */ 29 | protected $root_uri; 30 | 31 | /** 32 | * If There Must Check Paths 33 | * 34 | * it will Be true if Root_Rel path Not '/' 35 | * 36 | * @var Boolean 37 | */ 38 | protected $must_check; 39 | 40 | 41 | /** 42 | * Constructor 43 | * 44 | * @param String $root_uri Root relative path 45 | */ 46 | public function __construct( $root_uri ) 47 | { 48 | if( App::has( 'uri' ) ) 49 | throw new RuntimeException( __METHOD__ . ' Canot Instantiate Another Session Instance, There Is Already Instantiated One, Call App::get( "ur" )' ); 50 | 51 | $this->root_uri = $root_uri; 52 | 53 | $this->must_check = ( $this->root_uri == '/' ) ? false : true; 54 | 55 | App::register( 'url', $this ); 56 | 57 | // Set Instance On Url Class 58 | Redirect::setUrlInstance( $this ); 59 | 60 | } 61 | 62 | /** 63 | * Get Right Url path for given Uri path 64 | * 65 | * @param string $uri Uri path 66 | * @return string Right Uri Path 67 | */ 68 | public function to( $uri='' ) 69 | { 70 | return ( $this->must_check ) ? $this->root_uri . ltrim( $uri, '/' ) : $uri; 71 | } 72 | 73 | /** 74 | * Prefix Path For assets Directory 75 | * 76 | * @param string $uri Uri path 77 | * @return string Right Uri Path 78 | */ 79 | public function assets( $uri='' ) 80 | { 81 | return $this->root_uri . 'assets/' . ltrim( $uri, '/' ); 82 | } 83 | 84 | 85 | /** 86 | * Prefix Path For Css Directory 87 | * 88 | * @param string $css_file Uri path 89 | * @return string Right Uri Path 90 | */ 91 | public function style( $css_file='' ) 92 | { 93 | return $this->root_uri . 'assets/css/' . ltrim( $css_file, '/' ); 94 | } 95 | 96 | 97 | /** 98 | * Geth Full Html Link element For Given css File 99 | * 100 | * @param string $csss_file Css File 101 | * @return string Html Link element 102 | */ 103 | public function htmlLink( $csss_file='' ) 104 | { 105 | return ''; 106 | } 107 | 108 | 109 | /** 110 | * Prefix Path For Js Directory 111 | * 112 | * @param string $js_file Uri path 113 | * @return string Right Uri Path 114 | */ 115 | public function script( $js_file='' ) 116 | { 117 | return $this->root_uri . 'assets/js/' . ltrim( $js_file, '/' ); 118 | } 119 | 120 | 121 | /** 122 | * Geth Full Html Script element For Given Js File 123 | * 124 | * @param string $js_file Css File 125 | * @return string Html Script Element 126 | */ 127 | public function htmlScript( $js_file='' ) 128 | { 129 | return ''; 130 | } 131 | 132 | 133 | /** 134 | * Check Given Path If Needed To Be Prefixed By rooteRel Or Not 135 | * 136 | * @param string $uri Uri Path 137 | * @return string Right Uri Path 138 | */ 139 | public function check( $uri='' ) 140 | { 141 | if( $uri == '#' || ( strpos( $uri, $this->root_uri ) === 0 ) ) 142 | return $uri; 143 | else 144 | return $this->root_uri . ltrim( $uri, '/' ); 145 | } 146 | 147 | } -------------------------------------------------------------------------------- /App/Library/Validator.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt CC-BY-SA-4.0 Creative Commons Attribution Share Alike 4.0 10 | * 11 | * @property-read Array $inputs Inputs array 12 | * @property-read Array $rules Input rules Array 13 | * @property-read Array $required Required Fields Array 14 | * @property-read App\Library\ErrorsBag $errors Validation error bag 15 | */ 16 | 17 | namespace App\Library; 18 | 19 | use Exception; 20 | use App\Library\App; 21 | use App\Library\ErrorsBag; 22 | use App\Library\MagicGetterTrait; 23 | 24 | class Validator{ 25 | 26 | use MagicGetterTrait; 27 | 28 | /** 29 | * Inputs To validate 30 | * @var Array 31 | */ 32 | protected $inputs; 33 | 34 | 35 | /** 36 | * Input Rules To Perform Validation 37 | * @var Array 38 | */ 39 | protected $rules; 40 | 41 | 42 | /** 43 | * Required Fields Array 44 | * @var Array 45 | */ 46 | protected $required; 47 | 48 | 49 | /** 50 | * Validation ErrorsBag 51 | * @var App\Library\ErrorsBag 52 | */ 53 | protected $errors; 54 | 55 | /** 56 | * Validation Custom Messages 57 | * @var Array 58 | */ 59 | protected $messages; 60 | 61 | 62 | /** 63 | * Constructor 64 | * 65 | * @param Array $inputs Inputs Array 66 | * @param Array $rules Inputs rules To Validate According To 67 | * @param Array $custom_messages Custom Error Messages 68 | */ 69 | public function __construct( Array $inputs, Array $rules, Array $custom_messages = null ) 70 | { 71 | if( !is_array( $inputs ) ) 72 | throw new Exception( __METHOD__ . 'First Argument represents inputs must be Array.' ); 73 | 74 | if( !is_array( $rules ) ) 75 | throw new Exception( __METHOD__ . 'Second Argument represents rules must be Array.' ); 76 | 77 | // Set inputs 78 | $this->inputs = $inputs; 79 | 80 | // $this->errors = []; 81 | $this->errors = new ErrorsBag(); 82 | $this->required = []; 83 | 84 | // Set Up Custom Messages 85 | $this->messages = []; 86 | 87 | if( $custom_messages ) 88 | { 89 | foreach( $custom_messages as $input_rule => $message ) 90 | { 91 | // Get Input, Rule 92 | $parts = explode( '.', $input_rule ); 93 | $input = $parts[0]; 94 | $rule = isset( $parts[1] ) ? $parts[1] : '*'; 95 | 96 | if( isset( $this->messages[ $input ] ) ) 97 | $this->messages[ $input ][ $rule ] = $message; 98 | else 99 | $this->messages[ $input ] = [ $rule => $message ]; 100 | } 101 | } 102 | 103 | 104 | // Set Rules 105 | foreach( $rules as $input => $rule ) 106 | { 107 | if( is_array( $rule ) ) 108 | $this->rules[ $input ] = $rule; 109 | else 110 | { 111 | $rule = explode( '|', trim( $rule, '|' ) ); 112 | $this->rules[ $input ] = $rule; 113 | } 114 | 115 | // Get Required 116 | if( in_array( 'required', $this->rules[ $input ] ) ) 117 | array_push( $this->required, $input ); 118 | 119 | // Check Options 120 | foreach( $this->rules[ $input ] as &$r ) 121 | { 122 | $parts = explode( ':', $r ); 123 | 124 | if( count( $parts ) > 1 ) 125 | { 126 | // Has Options 127 | $r = [ $parts[0] => explode( ',', $parts[1] ) ]; 128 | 129 | // Validate 130 | $options = $r[ $parts[0] ]; 131 | $this->{$parts[0]}( $input, $options ); 132 | } 133 | else 134 | // validate 135 | $this->$r( $input ); 136 | 137 | } 138 | } 139 | 140 | // echo '
'; 141 | // print_r( $this ); 142 | } 143 | 144 | /** 145 | * Instantiate Validator Instance 146 | * 147 | * @param Array $inputs Inputs Array 148 | * @param Array $rules Inputs rules To Validate According To 149 | * @param Array $custom_messages Custom Error Messages 150 | * 151 | * @return Validator Instance 152 | */ 153 | public static function make( Array $inputs, Array $rules, Array $custom_messages = null ) 154 | { 155 | return new self( $inputs, $rules, $custom_messages ); 156 | } 157 | 158 | 159 | /** 160 | * Check If Input Exists In Inputs array 161 | * 162 | * @param string $input Input name 163 | * @return boolean True If Exists Otherwise False 164 | */ 165 | public function hasInput( $input ) 166 | { 167 | return isset( $this->inputs[ $input ] ); 168 | } 169 | 170 | 171 | /** 172 | * CHECK REQUIRED RULE 173 | * 174 | * @param string $input Input name 175 | */ 176 | public function required( $input ) 177 | { 178 | if( !$this->hasInput( $input ) ) 179 | $this->errors->add( $input, [ 'required' , $this->getMessage( $input, 'required', 'Required Filed.' ) ] ); 180 | } 181 | 182 | 183 | /** 184 | * CHECK FILLED RULE 185 | * 186 | * @param string $input Input name 187 | */ 188 | public function filled( $input ) 189 | { 190 | if( $this->mustCheck( $input ) ) 191 | { 192 | if( empty( $this->inputs[ $input ] ) ) 193 | $this->errors->add( $input, [ 'filled' , $this->getMessage( $input, 'filled', 'Required And Not Empty Field.' ) ] ); 194 | } 195 | } 196 | 197 | 198 | /** 199 | * CHECK Numeric RULE 200 | * 201 | * @param string $input Input name 202 | */ 203 | public function numeric( $input ) 204 | { 205 | if( $this->mustCheck( $input ) ) 206 | { 207 | if( !$this->hasInput( $input ) || !is_numeric( $this->inputs[ $input ] ) ) 208 | $this->errors->add( $input, [ 'numeric' , $this->getMessage( $input, 'numeric', 'Must Be Numeric Value.' ) ] ); 209 | } 210 | } 211 | 212 | 213 | /** 214 | * CHECK INTEGER RULE 215 | * 216 | * @param string $input Input name 217 | */ 218 | public function integer( $input ) 219 | { 220 | if( $this->mustCheck( $input ) ) 221 | { 222 | if( !$this->hasInput( $input ) || filter_var( $this->inputs[ $input ], FILTER_VALIDATE_INT ) === false ) 223 | $this->errors->add( $input, [ 'integer' , $this->getMessage( $input, 'integer', 'Must Be Integer Value.' ) ] ); 224 | } 225 | } 226 | 227 | 228 | /** 229 | * CHECK STRING RULE 230 | * 231 | * @param string $input Input name 232 | */ 233 | public function string( $input ) 234 | { 235 | if( $this->mustCheck( $input ) ) 236 | { 237 | if( !$this->hasInput( $input ) || is_numeric( $this->inputs[ $input ] ) ) 238 | $this->errors->add( $input, [ 'string' , $this->getMessage( $input, 'string', 'Must Be String Value.' ) ] ); 239 | } 240 | } 241 | 242 | 243 | /** 244 | * CHECK MIN RULE 245 | * 246 | * @param string $input Input name 247 | */ 248 | public function min( $input, $options ) 249 | { 250 | if( empty( $options ) ) 251 | throw new Exception( __METHOD__ . ' Rule Must Have Min Value' ); 252 | 253 | if( $this->mustCheck( $input ) ) 254 | { 255 | if( !$this->hasInput( $input ) ) 256 | // $this->errors->add( $input, [ 'min' , 'Must Be Greater Than '. $options[0] ] ); 257 | $this->errors->add( $input, [ 'min', $this->getMessage( $input, 'min' , 'Must Be Greater Than '. $options[0] ) ] ); 258 | else 259 | { 260 | if( is_numeric( $this->inputs[ $input ] ) ) 261 | { 262 | if( $this->inputs[ $input ] < $options[0] ) 263 | $this->errors->add( $input, [ 'min', $this->getMessage( $input, 'min' , 'Must Be Greater Than '. $options[0] ) ] ); 264 | // $this->errors->add( $input, [ 'min' , 'Must Be Greater Than '. $options[0] ] ); 265 | } 266 | else if( strlen( $this->inputs[ $input ] ) < $options[0] ) 267 | $this->errors->add( $input, [ 'min', $this->getMessage( $input, 'min' , 'Must Be Greater Than '. $options[0] ) ] ); 268 | // $this->errors->add( $input, [ 'min' , 'Length Must Be Greater Than '. $options[0] ] ); 269 | 270 | } 271 | } 272 | 273 | } 274 | 275 | 276 | /** 277 | * CHECK MAX RULE 278 | * 279 | * @param string $input Input name 280 | */ 281 | public function max( $input, $options ) 282 | { 283 | if( empty( $options ) ) 284 | throw new Exception( __METHOD__ . ' Rule Must Have Max Value' ); 285 | 286 | if( $this->mustCheck( $input ) ) 287 | { 288 | if( !$this->hasInput( $input ) ) 289 | $this->errors->add( $input, [ 'max' , $this->getMessage( $input, 'max', 'Must Be Less Than '. $options[0] ) ] ); 290 | else 291 | { 292 | if( is_numeric( $this->inputs[ $input ] ) ) 293 | { 294 | if( $this->inputs[ $input ] > $options[0] ) 295 | $this->errors->add( $input, [ 'max' , $this->getMessage( $input, 'max', 'Must Be Less Than '. $options[0] ) ] ); 296 | } 297 | else if( strlen( $this->inputs[ $input ] ) > $options[0] ) 298 | $this->errors->add( $input, [ 'max' , $this->getMessage( $input, 'max', 'Length Must Be Less Than '. $options[0] ) ] ); 299 | } 300 | } 301 | 302 | } 303 | 304 | 305 | /** 306 | * CHECK BETWEEN RULE 307 | * 308 | * @param string $input Input name 309 | */ 310 | public function between( $input, Array $options ) 311 | { 312 | if( count( $options ) < 2 ) 313 | throw new Exception( __METHOD__ . ' Rule Must Have Min and Max' ); 314 | 315 | if( $options[1] > $options[0] ) 316 | { 317 | $this->min( $input, [ $options[0] ] ); 318 | $this->max( $input, [ $options[1] ] ); 319 | } 320 | else 321 | { 322 | $this->max( $input, [ $options[0] ] ); 323 | $this->min( $input, [ $options[1] ] ); 324 | } 325 | 326 | } 327 | 328 | 329 | /** 330 | * CHECK EMAIL RULE 331 | * 332 | * @param string $input Input name 333 | */ 334 | public function email( $input ) 335 | { 336 | if( $this->mustCheck( $input ) ) 337 | { 338 | if( !$this->hasInput( $input ) || filter_var( $this->inputs[ $input ], FILTER_VALIDATE_EMAIL ) === false ) 339 | $this->errors->add( $input, [ 'email' , $this->getMessage( $input, 'email', 'Not Valid Email Address' ) ] ); 340 | } 341 | } 342 | 343 | 344 | /** 345 | * CHECK BOOLEAN RULE 346 | * 347 | * @param string $input Input name 348 | */ 349 | public function boolean( $input ) 350 | { 351 | if( $this->mustCheck( $input ) ) 352 | { 353 | if( !$this->hasInput( $input ) || filter_var( $this->inputs[ $input ], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ) === null ) 354 | $this->errors->add( $input, [ 'boolean' , $this->getMessage( $input, 'boolean', 'Must Be Boolean Value '. $options[0] ) ] ); 355 | } 356 | } 357 | 358 | /** 359 | * CHECK Array RULE 360 | * 361 | * @param string $input Input name 362 | */ 363 | public function array( $input ) 364 | { 365 | if( $this->mustCheck( $input ) ) 366 | { 367 | if( !$this->hasInput( $input ) || !is_array( $this->inputs[ $input ] ) ) 368 | $this->errors->add( $input, [ 'array' , $this->getMessage( $input, 'array', 'Must Be Of Type Array' ) ] ); 369 | } 370 | } 371 | 372 | 373 | /** 374 | * CHECK UNIQUE RULE 375 | * 376 | * @param string $input Input name 377 | */ 378 | public function unique( $input, $options ) 379 | { 380 | if( $this->mustCheck( $input ) ) 381 | { 382 | $count = count( $options ); 383 | if( $count < 2 ) 384 | throw new Exception( __METHOD__ . ' Requires Table name And Column, And Optionally ID To Ignore From Uniqness' ); 385 | 386 | $pdo = App::get('DBH' ); 387 | 388 | $sql = 'SELECT * FROM `' . $options[0] . '` WHERE (`' . $options[1] . '` = :val)'; 389 | 390 | if( $count > 2 ) 391 | { 392 | $ids = join( ',', array_slice( $options, 2 ) ); 393 | // $ids = join( ' OR `id` <> ', array_slice( $options, 2 ) ) . ')'; 394 | 395 | $sql .= ' AND `id` NOT IN (' . $ids . ')'; 396 | // $sql .= ' AND ( `id` <>' . $ids; 397 | // $sql .= ' WHERE `id` NOT IN ( SELECT `id` FROM `' . $options[0] . '` WHERE `id` IN (' . $ids . '))'; 398 | } 399 | 400 | // echo $sql; 401 | // echo '# | 6 |User Name | 7 |User Email | 8 |is Admin | 9 |10 | |
---|---|---|---|---|
echo $i; ?> | 16 |echo $user->username; ?> | 17 |echo $user->email; ?> | 18 |echo $user->admin ? 1 : 0; ?> | 19 |20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | | 30 |