├── .env ├── main ├── template │ ├── model │ │ └── user.php │ ├── controller │ │ └── userController.php │ ├── .env │ ├── public │ │ ├── index.php │ │ └── .htaccess │ └── route │ │ └── route.php ├── providers │ └── Enum.php ├── autoload-cli.php ├── console.php └── InitProject.php ├── zote ├── .gitignore ├── composer.json ├── remoteDevice.php ├── Main.php ├── providers ├── response.php ├── Html.php ├── uploader.php ├── ExceptionHandler.php ├── env.php ├── Notifier.php ├── request.php ├── validation.php └── S3.php ├── LICENSE ├── hash.php ├── schema ├── database.php └── table.php ├── README.md ├── auth.php └── app.php /.env: -------------------------------------------------------------------------------- 1 | DB_NAME=hellowrld -------------------------------------------------------------------------------- /main/template/model/user.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/template/controller/userController.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/template/.env: -------------------------------------------------------------------------------- 1 | DB_SERVER=localhost 2 | DB_USER=root 3 | DB_PASSWORD=password 4 | DB_NAME=zote -------------------------------------------------------------------------------- /zote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | -------------------------------------------------------------------------------- /main/providers/Enum.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/template/public/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # These are some examples of commonly ignored file patterns. 2 | # You should customize this list as applicable to your project. 3 | # Learn more about .gitignore: 4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 5 | 6 | # Node artifact files 7 | node_modules/ 8 | applications/ 9 | .DS_Store -------------------------------------------------------------------------------- /main/autoload-cli.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloakn/zframework", 3 | "description": "Zframework , it is php framework.", 4 | "type": "library", 5 | "require": { 6 | "php": ">=7.0.2" 7 | }, 8 | "license": "proprietary", 9 | "authors": [ 10 | { 11 | "name": "AKN", 12 | "email": "aungkyawnyunt2004@gmail.com" 13 | } 14 | ], 15 | "minimum-stability": "dev", 16 | "autoload": { 17 | "psr-4": { 18 | "zframework\\": "./" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /remoteDevice.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/template/public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | -------------------------------------------------------------------------------- /main/template/route/route.php: -------------------------------------------------------------------------------- 1 | addroute('post','/','',function(){ 5 | echo "hello"; 6 | }); 7 | $route->addroute('post','/direct','',function(){ 8 | echo "hello direct"; 9 | }); 10 | /* 11 | route with controller and function 12 | controller : UserController 13 | function : testFun 14 | */ 15 | $route->addroute('get','/test','UserController','testFun'); 16 | 17 | /* 18 | route prefix 19 | */ 20 | $route->routePrefix("/demo",function($route){ 21 | /* /demo/d1 */ 22 | $route->addroute('get','/d1','DemoController','d1Fun'); 23 | /* /demo/d2 */ 24 | $route->addroute('get','/d2','DemoController','d2Fun'); 25 | 26 | //nested route prefix 27 | $route->routePrefix("/dx",function($route){ 28 | /* /demo/dx/dx1 */ 29 | $route->addroute('get','/dx1','DemoController','dx1Fun'); 30 | }); 31 | $route->routePrefix("/dy",function($route){ 32 | /* /demo/dy/dy1 */ 33 | $route->addroute('get','/dy1','DemoController','dy1Fun'); 34 | }); 35 | }); 36 | $route->addroute('get','hello','DemoController','d1Fun'); 37 | ?> -------------------------------------------------------------------------------- /Main.php: -------------------------------------------------------------------------------- 1 | $name($arguments); 19 | } 20 | else{ 21 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 22 | } 23 | } 24 | public static function __callStatic($name, $arguments) { 25 | //print_r(static::$_instance );exit; 26 | return static::$_instance->$name($arguments); 27 | /* 28 | if(in_array($name,$functionList)){ 29 | return self::$_instance->$name($arguments); 30 | } 31 | else{ 32 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound(self::$_instance,$name)); 33 | }*/ 34 | } 35 | } 36 | 37 | ?> -------------------------------------------------------------------------------- /providers/response.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Aung Kyaw Nyunt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hash.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /providers/Html.php: -------------------------------------------------------------------------------- 1 | html/login.php 18 | 19 | include((file_exists($f.".php")?$f.".php":$f.".html.php")); 20 | 21 | } 22 | public function __call($name, $arguments) { 23 | $name = "_".$name; 24 | $functionList = get_class_methods($this); 25 | if(in_array($name,$functionList)){ 26 | return $this->$name($arguments); 27 | } 28 | else{ 29 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 30 | } 31 | } 32 | public static function __callStatic($name, $arguments) { 33 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 34 | $name = "_".$name; 35 | $functionList = get_class_methods(self::$_instance); 36 | if(in_array($name,$functionList)){ 37 | return self::$_instance->$name($arguments); 38 | } 39 | else{ 40 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 41 | } 42 | } 43 | } 44 | 45 | //echo Env::get('DB_NAME'); 46 | ?> -------------------------------------------------------------------------------- /providers/uploader.php: -------------------------------------------------------------------------------- 1 | "; 25 | //echo json_encode($arguments); 26 | $functionList = get_class_methods($this); 27 | if(in_array($name,$functionList)){ 28 | return $this->$name($arguments); 29 | } 30 | else{ 31 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 32 | } 33 | } 34 | 35 | public static function __callStatic($name, $arguments) { 36 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 37 | $name = "_".strtolower($name); 38 | $functionList = get_class_methods(self::$_instance); 39 | if(in_array($name,$functionList)){ 40 | return self::$_instance->$name($arguments); 41 | } 42 | else{ 43 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 44 | } 45 | } 46 | } 47 | 48 | ?> -------------------------------------------------------------------------------- /main/console.php: -------------------------------------------------------------------------------- 1 | "\e[39m", 17 | "Black" => "\e[30m", 18 | "Red" => "\e[31m", 19 | "Green" => "\e[32m", 20 | "Yellow" => "\e[33m", 21 | "Blue" => "\e[34m", 22 | "Magenta" => "\e[35m", 23 | "Cyan" => "\e[36m", 24 | "LightGray" => "\e[37m", 25 | "DarkGray" => "\e[90m", 26 | "LightRed" => "\e[91m", 27 | "LightGreen" => "\e[92m", 28 | "LightYellow" => "\e[93m", 29 | "LightBlue" => "\e[94m", 30 | "LightMagenta" => "\e[95m", 31 | "LightCyan" => "\e[96m", 32 | "White" => "\e[97m", 33 | ); 34 | 35 | function __construct($argv) { 36 | $this->argv = $argv; 37 | $argCount = count($this->argv); 38 | if($argCount>1){ 39 | 40 | } 41 | else{ 42 | echo "What u want to do? \n"; 43 | echo $this->_echo("Yellow","You need argument\n"); 44 | echo $this->_echo("LightCyan","php zote start?\n"); 45 | exit(); 46 | } 47 | } 48 | private function _echo($color,$text,$newLine='yes'){ 49 | echo $this->color[$color].$text.($newLine=='no'?"":"\n"); 50 | echo $this->color['White']; 51 | } 52 | 53 | function start(){ 54 | #echo "starting"; 55 | echo $this->_echo("Blue",$command); 56 | $command = $this->argv[1]; 57 | switch($command){ 58 | case 'init': 59 | 60 | $initPro = new InitProject($this->argv); 61 | break; 62 | } 63 | } 64 | } 65 | 66 | $engine = new Engine($argv); 67 | $engine->start(); 68 | # php zote-framework/zote init 69 | ?> -------------------------------------------------------------------------------- /providers/ExceptionHandler.php: -------------------------------------------------------------------------------- 1 | Function Not Found Exception
"; 18 | echo "
".get_class($me).":".$name." NOT FOUND
"; 19 | $functionList = get_class_methods($me); 20 | foreach($route as $k=>$v){ 21 | echo "
".$k." : ".$v."
"; 22 | } 23 | exit(); 24 | } 25 | static function MethodNotFound($me,$name){ 26 | //throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,"methodname")); 27 | echo "
Method Not Found Exception
"; 28 | echo "
".get_class($me).":".$name."
"; 29 | $functionList = get_class_methods($me); 30 | foreach($functionList as $k=>$v){ 31 | echo "
".get_class($me).":".$v."
"; 32 | } 33 | exit(); 34 | } 35 | static function RouteNotFound($url,$routeList){ 36 | //throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,"methodname")); 37 | echo "
Route Not Found Exception
"; 38 | echo "
not found ".$url." in route list
"; 39 | echo "
Route List :"; 40 | foreach($routeList as $k=>$v){ 41 | echo "
[".$v['method']."]:".$k."
"; 42 | } 43 | echo "
"; 44 | exit(); 45 | } 46 | 47 | 48 | 49 | } 50 | ?> -------------------------------------------------------------------------------- /providers/env.php: -------------------------------------------------------------------------------- 1 | $name($arguments); 45 | } 46 | else{ 47 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 48 | } 49 | } 50 | public static function __callStatic($name, $arguments) { 51 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 52 | $name = "_".$name; 53 | $functionList = get_class_methods(self::$_instance); 54 | if(in_array($name,$functionList)){ 55 | return self::$_instance->$name($arguments); 56 | } 57 | else{ 58 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 59 | } 60 | } 61 | } 62 | 63 | //echo Env::get('DB_NAME'); 64 | ?> -------------------------------------------------------------------------------- /main/InitProject.php: -------------------------------------------------------------------------------- 1 | $args = $args; 8 | if(count($this->$args)>=2){ 9 | $this->cmd = $this->$args[1]; 10 | //echo $this->cmd; 11 | //echo dirname(__FILE__);exit; 12 | $this->custom_copy(dirname(__FILE__)."/template",'./'); 13 | } 14 | else{ 15 | return false; 16 | } 17 | /* 18 | foreach($this->$args as $arg){ 19 | $cmd = explode(":",$arg); 20 | 21 | if(count($cmd)>1){ 22 | if($cmd[0]=="doc"){ 23 | $doc = $cmd[1]; 24 | $this->custom_copy(dirname(__FILE__)."/template",$doc); 25 | } 26 | } 27 | } 28 | */ 29 | } 30 | function custom_copy($src, $dst) { 31 | 32 | // open the source directory 33 | $dir = opendir($src); 34 | 35 | // Make the destination directory if not exist 36 | $this->createDir($dst); 37 | 38 | // Loop through the files in source directory 39 | while( $file = readdir($dir) ) { 40 | 41 | if (( $file != '.' ) && ( $file != '..' )) { 42 | if ( is_dir($src . '/' . $file) ) 43 | { 44 | // Recursively calling custom copy function 45 | // for sub directory 46 | $this->custom_copy($src . '/' . $file, $dst . '/' . $file); 47 | } 48 | else { 49 | copy($src . '/' . $file, $dst . '/' . $file); 50 | } 51 | } 52 | } 53 | closedir($dir); 54 | } 55 | private function createDir($dir){ 56 | // echo $dir;exit; 57 | //var_dump(is_dir($dir));exit; 58 | if(!is_dir($dir)){ 59 | //echo "created"; 60 | mkdir($dir); 61 | } 62 | } 63 | 64 | public function initEnv($file){ 65 | $file = fopen($file, "w") or die("Unable to open file!"); 66 | fwrite($file, "DB_SERVER=localhost\n"); 67 | fwrite($file, "DB_NAME=zote\n"); 68 | fwrite($file, "DB_USER=root\n"); 69 | fwrite($file, "DB_PASSWORD=\n"); 70 | fclose($file); 71 | } 72 | } 73 | ?> -------------------------------------------------------------------------------- /providers/Notifier.php: -------------------------------------------------------------------------------- 1 | key; 17 | } 18 | function _sendSMS($args){ 19 | //return "hello"; 20 | $host = self::$_host;//"elb-search-notification-1f8d31b3f685159b.elb.ap-southeast-2.amazonaws.com"; 21 | $port = self::$_port;//12345; 22 | 23 | $data = array( 24 | "key" => "3adfasdfasdfasdasdfasdf24234234dfasdf32rawsdfasdfq32radfasdfasdf", 25 | "type" => "from-notifier", 26 | "target" =>"SGW", // SGW for sms, NGW for noti 27 | //"data" => $argv[2] 28 | "data" => array( 29 | "phNo" => $args[0],//$argv[3],//"+95975", 30 | "message" => $args[1] //message 31 | ) 32 | ); 33 | $message = base64_encode(json_encode($data)); 34 | $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Could not create socket\n"); 35 | // connect to server 36 | $result = socket_connect($socket, $host, $port) or die("Could not connect to server\n"); 37 | // send string to server 38 | socket_write($socket, $message, strlen($message)) or die("Could not send data to server\n"); 39 | // close socket 40 | socket_close($socket); 41 | return $data; 42 | } 43 | 44 | 45 | public function __call($name, $arguments) { 46 | $name = "_".$name; 47 | $functionList = get_class_methods($this); 48 | if(in_array($name,$functionList)){ 49 | return $this->$name($arguments); 50 | } 51 | else{ 52 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 53 | } 54 | } 55 | 56 | public static function __callStatic($name, $arguments) { 57 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 58 | $name = "_".$name; 59 | $functionList = get_class_methods(self::$_instance); 60 | if(in_array($name,$functionList)){ 61 | return self::$_instance->$name($arguments); 62 | } 63 | else{ 64 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 65 | } 66 | } 67 | } 68 | ?> -------------------------------------------------------------------------------- /providers/request.php: -------------------------------------------------------------------------------- 1 | db = $db; 18 | } 19 | else{ 20 | //echo "-"; 21 | //echo "Request "; 22 | $this->db = Database::Instance(); 23 | } 24 | 25 | } 26 | private function getFilter($arr){ 27 | if(!is_array($arr)){ 28 | $arr = $this->db->conn->real_escape_string($arr); 29 | } 30 | else{ 31 | foreach($arr as $k => $v){ 32 | $arr[$k] = is_array($arr[$k]) ? $this->getFilter($arr[$k]) : $this->db->conn->real_escape_string($arr[$k]); 33 | } 34 | } 35 | return $arr; 36 | } 37 | function _getfile($args){ 38 | //print_r($args);exit; 39 | $index = $args[0]; 40 | //print_r($index);exit; 41 | $data = array_key_exists($index,$_FILES)? 42 | $_FILES[$index]:NULL; 43 | //print_r($data); 44 | return $data; 45 | } 46 | function _getfiles($args){ 47 | //print_r($args);exit; 48 | $index = $args[0]; 49 | //print_r($index);exit; 50 | $data = array_key_exists($index,$_FILES)? 51 | $_FILES[$index]:NULL; 52 | // /print_r($data);exit; 53 | return $data; 54 | } 55 | function _get($args){ 56 | $index = $args[0]; 57 | $data = array_key_exists($index,$_GET)? 58 | $this->getFilter($_GET[$index]): 59 | (array_key_exists($index,$_POST)?$this->getFilter($_POST[$index]):NULL); 60 | 61 | return $data; 62 | } 63 | //default functions 64 | public function __call($name, $arguments) { 65 | $name = "_".strtolower($name); 66 | $functionList = get_class_methods($this); 67 | if(in_array($name,$functionList)){ 68 | return $this->$name($arguments); 69 | } 70 | else{ 71 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 72 | } 73 | } 74 | 75 | public static function __callStatic($name, $arguments) { 76 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 77 | self::$_instance->db = (self::$_instance->db === null ? new Database() : self::$_instance->db); 78 | $name = "_".strtolower($name); 79 | $functionList = get_class_methods(self::$_instance); 80 | if(in_array($name,$functionList)){ 81 | return self::$_instance->$name($arguments); 82 | } 83 | else{ 84 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 85 | } 86 | } 87 | //end default functions 88 | } 89 | ?> -------------------------------------------------------------------------------- /schema/database.php: -------------------------------------------------------------------------------- 1 | connectDB(); 16 | } 17 | function connectDB(){ 18 | // echo "connectDB"; 19 | 20 | $servername = Env::get('DB_SERVER'); 21 | //echo Env::get("DB_SERVER");exit; 22 | $username = Env::get('DB_USER'); 23 | $password = Env::get('DB_PASSWORD'); 24 | $db =Env::get('DB_NAME'); 25 | self::$_instance = $this; 26 | self::$_instance->conn = new \mysqli($servername, $username, $password,$db); 27 | if (self::$_instance->conn->connect_error) { 28 | die("Connection failed: " . $conn->connect_error); 29 | exit(); 30 | } 31 | else{ 32 | // echo “conn success”; 33 | } 34 | 35 | } 36 | static function Instance(){ 37 | //echo "Instance"; 38 | 39 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 40 | //self::$_instance->connectDB(); 41 | 42 | return self::$_instance; 43 | } 44 | static function query($cmdString,$isStoredProcedure=false){ 45 | //echo “i”; 46 | return self::$_instance->conn->query($cmdString); 47 | if($isStoredProcedure){ 48 | $this->connectDB(); 49 | } 50 | } 51 | static function fetchAllQuery($cmdString){ 52 | //echo “i”; 53 | $result = self::$_instance->conn->query($cmdString); 54 | //return $cmdString; 55 | //return $result; 56 | if($result){ 57 | $data = []; 58 | while($row = $result->fetch_assoc()) { 59 | $data[] = $row; 60 | } 61 | return $data; 62 | } 63 | else{ 64 | return false; 65 | } 66 | } 67 | 68 | static function executeQueryPaginate($cmdString,$page_at,$row_count){ 69 | //echo “i”; 70 | //$page_at -= 1; 71 | $result = self::$_instance->conn->query($cmdString); 72 | 73 | $totalRecords = $result->num_rows; 74 | $total_page = $totalRecords/ $row_count; 75 | 76 | $from = ($page_at*$row_count)-$row_count; 77 | 78 | 79 | $paginate = array( 80 | "page_at" => $page_at, 81 | "total_page" => ceil($total_page), 82 | "total_records_count" => $totalRecords 83 | ); 84 | //var_dump($row_count);//exit; 85 | $paginatecmdString = $cmdString . " LIMIT $from,$row_count"; 86 | // echo $paginatecmdString; 87 | $result = self::$_instance->conn->query($paginatecmdString); 88 | 89 | $data = []; 90 | while($row = $result->fetch_assoc()) { 91 | $data[] = $row; 92 | } 93 | $returnData = new \stdClass(); 94 | $returnData->paginate = $paginate; 95 | $returnData->data = $data; 96 | return $returnData; 97 | 98 | 99 | return self::$_instance->conn->query($cmdString); 100 | if($isStoredProcedure){ 101 | $this->connectDB(); 102 | } 103 | } 104 | 105 | function close(){ 106 | //echo “i”; 107 | self::$_instance->conn->close(); 108 | $servername = Env::get('DB_SERVER'); 109 | $username = Env::get('DB_USER'); 110 | $password = Env::get('DB_PASSWORD'); 111 | $db =Env::get('DB_NAME'); 112 | self::$_instance->conn = new \mysqli($servername, $username, $password,$db); 113 | // self::$_instance->conn->initialize(); 114 | } 115 | } 116 | //$db = new Database(); 117 | ?> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zframework 2 | [![Star Count](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=Star&query=stargazers_count&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Fzframework)](https://github.com/helloakn/zframework) [![Licence](https://img.shields.io/badge/dynamic/json?color=informational&label=LICENCE&query=license.name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Fzframework)](https://github.com/helloakn/zframework) [![Language](https://img.shields.io/badge/dynamic/json?color=blueviolet&label=Language&query=language&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Fzframework)](https://github.com/helloakn/zframework) 3 | 4 | ## Create Project 5 | ``` 6 | php zframework/zote init 7 | ``` 8 | After init prject, file structure will be 9 | ``` 10 | [dir] zframework 11 | []... 12 | [dir] project 13 | [file] .env 14 | [dir] public 15 | [file] index.php 16 | [file] .htaccess 17 | [file] robots.txt 18 | [dir] mobile 19 | [file] User.php 20 | [dir] migration 21 | [file] createUser.php 22 | [dir] controller 23 | [file] userController.php 24 | [file] route.php 25 | ``` 26 | 27 | - - - 28 | 29 | ## Route 30 | * without controller 31 | * with controller 32 | * route prefix 33 | * route Authorization {with guard} 34 | ### Route Example 35 | * Route : without controller 36 | ```php 37 | addroute('post','/','',function(){ 40 | echo "hello"; 41 | }); 42 | $route->addroute('post','/direct','',function(){ 43 | echo "hello direct"; 44 | }); 45 | ?> 46 | ``` 47 | * Route : controller 48 | ```php 49 | addroute('get','/test','UserController','testFun'); 56 | ?> 57 | ``` 58 | 59 | * Route : Prefix 60 | ```php 61 | routePrefix("/demo",function($route){ 66 | /* /demo/d1 */ 67 | $route->addroute('get','/d1','DemoController','d1Fun'); 68 | /* /demo/d2 */ 69 | $route->addroute('get','/d2','DemoController','d2Fun'); 70 | 71 | //nested route prefix 72 | $route->routePrefix("/dx",function($route){ 73 | /* /demo/dx/dx1 */ 74 | $route->addroute('get','/dx1','DemoController','dx1Fun'); 75 | }); 76 | $route->routePrefix("/dy",function($route){ 77 | /* /demo/dy/dy1 */ 78 | $route->addroute('get','/dy1','DemoController','dy1Fun'); 79 | }); 80 | }); 81 | ?> 82 | ``` 83 | - - - 84 | ## Model 85 | * Create Model 86 | * Insert 87 | * Update 88 | * Delete 89 | * Select , Where , Group , Order 90 | 91 | ### Model Example 92 | 93 | * Create Model 94 | 95 | ```php 96 | 120 | ``` 121 | * Insert 122 | 123 | ```php 124 | itemId = $request->get('id'); 133 | $item->name = "Item One"; 134 | $item->save(); 135 | } 136 | } 137 | ?> 138 | ``` 139 | * Update 140 | 141 | ```php 142 | get('id')); 154 | $item->name = "ok"; 155 | $item->update(); 156 | /* OR */ 157 | /* second way */ 158 | $item = new Item(); 159 | $item->itemId = $request->get('id'); 160 | $item->name = "Item One"; 161 | $item->update(); 162 | return $item; 163 | } 164 | } 165 | ?> 166 | ``` 167 | * Select , Where , Group , Order 168 | 169 | ```php 170 | get(); 178 | //where 179 | $item = Item::select("id",'name')->where("id in (1,2)")->get(); 180 | //order by 181 | $item = Item::select("id",'name')->where("id in (1,2)")->orderBy("id desc")->get(); 182 | //group by 183 | $item = Item::select("SUM(qty) as quantity",'name')->where("id in (1,2,3,4)")->orderBy("name desc")->groupBy("name")->get(); 184 | 185 | //getAll 186 | $users = User::select("id","name")->getAll(); 187 | foreach($users as $user){ 188 | echo "name : ". $user['name'] .'
'; 189 | } 190 | 191 | //paginate 192 | $users = User::select("id","name") 193 | ->paginate($page_at-1,$row_count); 194 | 195 | 196 | $data = array( 197 | "code" => 200, 198 | "status" => "success", 199 | "message" => "success", 200 | "pagination" => $users->paginate, 201 | "data" => $users->data 202 | ); 203 | return $data; 204 | 205 | } 206 | } 207 | ?> 208 | ``` 209 | - - - 210 | 211 | ## Validation 212 | * rule 213 | * single file and multi files validation rule 214 | 215 | ### Validation Example 216 | * rule 217 | 218 | ```php 219 | field("name")->max(4,"the name should be under 200 length"); 228 | $validator->field("name")->min(2); 229 | $validator->field("address")->min(5)->notNull(); 230 | //custom validation rule 231 | $validator->field("address")->custom(function($validator) use ( $request){ 232 | if($request->get('address')!="Sanchaung"){ 233 | $validator->setError("Custom validation erro"); 234 | } 235 | }); 236 | }); 237 | 238 | $v = $validator->validate(); 239 | 240 | if(!$v){ 241 | $data = array( 242 | "status_code" => 400, 243 | "status" => "failed", 244 | "validate"=>$v, 245 | "errors" => $validator->error() 246 | ); 247 | return $data; 248 | } 249 | } 250 | } 251 | ?> 252 | ``` 253 | 254 | 255 | 256 | * single file and multi files validation rule 257 | 258 | ```php 259 | file("profile_image") 269 | ->extenstions("jpg","png") 270 | ->minetype("image/jpeg","image/png"); 271 | 272 | //multi files 273 | $validator->files("image") 274 | ->extenstions("jpg","png") 275 | ->minetype("image/jpeg","image/png"); 276 | }); 277 | 278 | $v = $validator->validate(); 279 | 280 | if(!$v){ 281 | $data = array( 282 | "status_code" => 400, 283 | "status" => "failed", 284 | "validate"=>$v, 285 | "errors" => $validator->error() 286 | ); 287 | return $data; 288 | } 289 | } 290 | } 291 | ?> 292 | ``` 293 | 294 | - - - 295 | -------------------------------------------------------------------------------- /auth.php: -------------------------------------------------------------------------------- 1 | 'User', 25 | 'User' =>[ 26 | 'userTable' => 'User', 27 | 'userFields' => ["id","email",'display_name'], 28 | 'tokenTable' => 'token:user_id', 29 | ], 30 | 'staf' =>[ 31 | 'userTable' => 'User', 32 | 'userFields' => ["id","email",'display_name'], 33 | 'tokenTable' => 'token:user_id', 34 | ], 35 | ];*/ 36 | protected $guards = []; 37 | 38 | private static $_instance = null; 39 | 40 | function __construct() { 41 | $this->database = Database::Instance(); 42 | $this->guards = require '../provider/authorization.php'; 43 | } 44 | static function guard($usr){ 45 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 46 | self::$_instance->defaultGuard = $usr; 47 | return self::$_instance; 48 | } 49 | 50 | function login($loginInfo){ 51 | $cmdString = ""; 52 | /* 53 | $id = (array_key_exists("email",$loginInfo) ? "email='". $loginInfo['email']."'" : 54 | (array_key_exists("phone",$loginInfo) ? "phone='".$loginInfo['phone']."'" : 55 | (array_key_exists("username",$loginInfo) ? "username='".$loginInfo['username']."'" : NULL))); 56 | */ 57 | foreach($loginInfo as $k=>$v){ 58 | $query = $k."='".$v."'"; 59 | $cmdString .= $cmdString=="" ? $query : " AND " . $query ; 60 | } 61 | $cmdString = " SELECT * FROM ". $this->guards[$this->defaultGuard]['userTable'] ." WHERE ".$cmdString." LIMIT 0,1"; 62 | // echo $cmdString; exit; 63 | $result = $this->database->query($cmdString); 64 | //ternary operator 65 | //return $result->num_rows==1?true:false; 66 | switch($result->num_rows){ 67 | case 0: 68 | return false; 69 | break; 70 | default: 71 | $authUsr = $this->generateAuthUser($result); 72 | $device = $this->generateDevice(); 73 | $token = $this->generateToken($authUsr,$device); 74 | return true; 75 | break; 76 | } 77 | 78 | } 79 | 80 | private function generateRandomString($length = 200) 81 | { 82 | $characters = '!@#$%^&*()_+-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 83 | $charactersLength = strlen($characters); 84 | $randomString = ''; 85 | for ($i = 0; $i < $length; $i++) { 86 | $randomString .= $characters[rand(0, $charactersLength - 1)]; 87 | } 88 | return $randomString; 89 | } 90 | 91 | function generateDevice(){ 92 | // echo "generate dev"; 93 | $ip = RemoteDevice::ip(); 94 | $deviceName = RemoteDevice::Device(); 95 | 96 | $query = "device_info='".$this->database->conn->real_escape_string($deviceName)."' AND ip='".$this->database->conn->real_escape_string($ip)."'"; 97 | //print_r($query);exit; 98 | // print_r($query); 99 | $device = Device::select("*")->where($query)->first(); 100 | //print_r($device->id);exit; 101 | if($device){ 102 | // echo "found dev"; 103 | return $device; 104 | } 105 | else{ 106 | // echo "not found dev"; 107 | $device = new Device(); 108 | $device->ip = $ip; 109 | $device->device_info = $deviceName; 110 | // print_r($device);exit; 111 | $device->save(); 112 | } 113 | // echo "end "; 114 | return $device; 115 | } 116 | 117 | function generateToken($usr,$device){ 118 | //echo $this->defaultGuard; 119 | // print_r($this->guards[$this->defaultGuard]);exit; 120 | $vToken = $this->generateRandomString(100); 121 | $tmp = explode(":",$this->guards[$this->defaultGuard]['tokenTable']); 122 | $tokenTable = $this->guards[$this->defaultGuard]['tokenModel']; 123 | $tokenID = $tmp[1]; 124 | //echo $tokenID;exit; 125 | //$token = new $this->guards[$this->defaultGuard]['tokenTable'][0](); 126 | $token = new $tokenTable(); 127 | $token->device_id = $device->id; 128 | $token->$tokenID = $usr->id; 129 | $token->token = $vToken; 130 | $token->save(); 131 | $this->token = $vToken; 132 | return $vToken; 133 | } 134 | 135 | function getAuthorizationHeader(){ 136 | $headers = null; 137 | if (isset($_SERVER['Authorization'])) { 138 | $headers = trim($_SERVER["Authorization"]); 139 | } 140 | else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI 141 | $headers = trim($_SERVER["HTTP_AUTHORIZATION"]); 142 | } elseif (function_exists('apache_request_headers')) { 143 | $requestHeaders = apache_request_headers(); 144 | // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization) 145 | $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders)); 146 | //print_r($requestHeaders); 147 | if (isset($requestHeaders['Authorization'])) { 148 | $headers = trim($requestHeaders['Authorization']); 149 | } 150 | } 151 | return $headers; 152 | } 153 | function getBearerToken() { 154 | $headers = $this->getAuthorizationHeader(); 155 | // HEADER: Get the access token from the header 156 | if (!empty($headers)) { 157 | if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) { 158 | return $matches[1]; 159 | } 160 | } 161 | return false; 162 | } 163 | function isLogin(){ 164 | $this->token = $this->getBearerToken(); 165 | $guards = $this->guards[$this->defaultGuard]; 166 | $userTable = $this->defaultGuard;//$guards['userTable']; //User 167 | $tokenTable = explode(":",$guards['tokenTable']); //Token:user_id 168 | $userFields = implode(",", $guards['userFields']); 169 | if($this->token){ 170 | //$cmdString = "SELECT ".$userTable.".* FROM ".$tokenTable[0].",".$userTable." WHERE ".$tokenTable[0].".".$tokenTable[1]."=".$userTable.".id and ".$tokenTable[0].".token='".$this->token."'"; 171 | $cmdString = "SELECT ".$userFields." FROM ".$userTable." WHERE id in(SELECT ".$tokenTable[0].".".$tokenTable[1]." FROM ".$tokenTable[0]." WHERE ".$tokenTable[0].".token='".$this->token."' AND deleted_at IS NULL)"; 172 | // echo $this->defaultGuard;exit; 173 | // echo $cmdString;exit; 174 | $result = $this->database->query($cmdString); 175 | $this->generateAuthUser($result); 176 | return $result->num_rows >= 1 ? true : false ; 177 | } 178 | else{ 179 | return false; 180 | } 181 | } 182 | private function generateAuthUser($result ){ 183 | $authUser = new \stdClass(); 184 | foreach($result as $k=>$v){ 185 | foreach($v as $key=>$value){ 186 | 187 | $authUser->$key = $value; 188 | } 189 | $this->authUser = $authUser; 190 | } 191 | return $authUser; 192 | } 193 | function authUser(){ 194 | 195 | $guards = $this->guards[$this->guards['default']]; 196 | $userTable = $guards['userTable']; //User 197 | $tokenTable = explode(":",$guards['tokenTable']); //Token:user_id 198 | $userFields = implode(",", $guards['userFields']); 199 | //echo $userFields; 200 | //return $userFields; 201 | if($this->token){ 202 | $cmdString = "SELECT ".$userFields." FROM ".$userTable." WHERE id in(SELECT ".$tokenTable[0].".".$tokenTable[1]." FROM ".$tokenTable[0]." WHERE ".$tokenTable[0].".token='".$this->token."')"; 203 | //echo $cmdString; 204 | $result = $this->database->query($cmdString); 205 | $authUser = new \stdClass(); 206 | if($result->num_rows >= 1){ 207 | return $this->generateAuthUser($result); 208 | } 209 | else{ 210 | return false; 211 | } 212 | } 213 | else{ 214 | return false; 215 | } 216 | } 217 | function getToken(){ 218 | 219 | 220 | if($this->token){ 221 | return $this->token; 222 | } 223 | else{ 224 | return false; 225 | } 226 | } 227 | } 228 | ?> -------------------------------------------------------------------------------- /providers/validation.php: -------------------------------------------------------------------------------- 1 | _keys)){ 23 | $values = Request::get($this->_keys); 24 | if(is_array($values)){ 25 | foreach($values as $i=>$v){ 26 | if($args[0]_isValidate = false; 28 | $this->_error[$this->_keys][] = count($args)==2 ? $args[1] : $this->_keys." must be under ".$args[0]; 29 | } 30 | } 31 | } 32 | 33 | } 34 | else{ 35 | $value = Request::get($this->_key); 36 | if($args[0]_isValidate = false; 38 | $this->_error[$this->_key][] = count($args)==2 ? $args[1] : $this->_key." must be under ".$args[0]; 39 | } 40 | } 41 | 42 | 43 | 44 | return $this; 45 | } 46 | function _notnull($args){ 47 | 48 | if(isset($this->_keys)){ 49 | $values = Request::get($this->_keys); 50 | //echo '===\n'; 51 | // print_r($this->_keys); 52 | // echo '---\n'; 53 | // print_r($values);//exit; 54 | if(is_array($values)){ 55 | foreach($values as $i=>$v){ 56 | if($v==null || $v==""){ 57 | $this->_isValidate = false; 58 | $this->_error[$this->_keys][$i] = count($args)==1 ? $args[0] : $this->_keys." [$i] should not be null"; 59 | } 60 | } 61 | } 62 | 63 | 64 | } 65 | else{ 66 | $value = Request::get($this->_key); 67 | if($value==null || $value==""){ 68 | $this->_isValidate = false; 69 | $this->_error[$this->_key][] = count($args)==1 ? $args[0] : $this->_key." should not be null"; 70 | } 71 | } 72 | 73 | return $this; 74 | } 75 | function _min($args){ 76 | 77 | if(isset($this->_keys)){ 78 | $values = Request::get($this->_keys); 79 | if(is_array($values)){ 80 | foreach($values as $i=>$v){ 81 | if($args[0]>strlen($v)){ 82 | $this->_isValidate = false; 83 | $this->_error[$this->_keys][] = count($args)==2 ? $args[1] : "Minimum length of ". $this->_keys." is ".$args[0]; 84 | } 85 | } 86 | } 87 | 88 | } 89 | else{ 90 | $value = Request::get($this->_key); 91 | if($args[0]>strlen($value)){ 92 | $this->_isValidate = false; 93 | $this->_error[$this->_key][] = count($args)==2 ? $args[1] : "Minimum length of ". $this->_key." is ".$args[0]; 94 | } 95 | } 96 | 97 | return $this; 98 | } 99 | 100 | function _minetype($args){ 101 | // print_r($args);exit; 102 | //print_r($this->_keyFile);exit; 103 | // print_r($args);exit; 104 | //print_r($this->_keyFile);exit; 105 | if(isset($this->_keyFile)){ 106 | $value = Request::getfile($this->_keyFile); 107 | //print($value);exit; 108 | //print_r($value['type']);exit; 109 | //print_r($args);exit; 110 | if($value){ 111 | if(!in_array($value['type'],$args)){ 112 | //print_r($value['type']); echo "\n"; print_r($args); 113 | //echo "failed type"; 114 | $this->_isValidate = false; 115 | $this->_error[$this->_keyFile][] = "not allow File Type"; 116 | } 117 | } 118 | else{ 119 | return $this; 120 | } 121 | } 122 | else{ 123 | $values = Request::getfile($this->_keyFiles); 124 | if($values){ 125 | foreach($values['type'] as $i=>$v){ 126 | //print_r($values['type'][$i]);exit; 127 | // echo $values['type'][$i]; echo "xxx\n"; 128 | if(!in_array( $values['type'][$i],$args)){ 129 | $this->_isValidate = false; 130 | $this->_error[$this->_keyFiles][] = "not allow File Type"; 131 | } 132 | } 133 | 134 | } 135 | else{ 136 | return $this; 137 | } 138 | } 139 | return $this; 140 | 141 | } 142 | 143 | function _extenstions($args){ 144 | // print_r($args);exit; 145 | //print_r($this->_keyFile);exit; 146 | // print_r($args);exit; 147 | //print_r($this->_keyFiles);exit; 148 | 149 | if(isset($this->_keyFile)){ 150 | $value = Request::getfile($this->_keyFile); 151 | if($value){ 152 | $file_parts = pathinfo($value['name']); 153 | if(array_key_exists('extenstion',$file_parts)){ 154 | if(!in_array($file_parts['extension'],$args)){ 155 | $this->_isValidate = false; 156 | $this->_error[$this->_keyFile][] = "not allow File Extenstion"; 157 | } 158 | } 159 | } 160 | else{ 161 | return $this; 162 | } 163 | } 164 | else{ 165 | $values = Request::getfile($this->_keyFiles); 166 | if($values){ 167 | foreach($values['name'] as $i=>$v){ 168 | // print_r($values['name'][$i]);exit; 169 | $file_parts = pathinfo($values['name'][$i]); 170 | if(array_key_exists('extenstion',$file_parts)){ 171 | if(!in_array($file_parts['extension'],$args)){ 172 | $this->_isValidate = false; 173 | $this->_error[$this->_keyFiles][] = "not allow File Extenstion"; 174 | } 175 | } 176 | 177 | } 178 | 179 | } 180 | else{ 181 | return $this; 182 | } 183 | } 184 | return $this; 185 | 186 | } 187 | 188 | function _file($args){ 189 | // print_r($args);exit; 190 | $this->_keyFile = $args[0]; 191 | //print_r($this->_keyFile);exit; 192 | return $this; 193 | 194 | } 195 | function _files($args){ 196 | //print_r($args);exit; 197 | $this->_keyFiles = $args[0]; 198 | //print_r($this->_keyFile);exit; 199 | return $this; 200 | 201 | } 202 | function _field($args){ 203 | $this->_key = $args[0]; 204 | return $this; 205 | } 206 | 207 | function _fields($args){ 208 | //print_r($args); 209 | $this->_keys = $args[0]; 210 | return $this; 211 | } 212 | 213 | function _error($args){ 214 | return $this->_error; 215 | } 216 | function _validate($args){ 217 | return $this->_isValidate; 218 | } 219 | 220 | function _seterror($args){ 221 | //print_r($this->_key); 222 | $this->_isValidate = false; 223 | if(isset($this->_keys)){ 224 | $this->_error[$this->_keys][] = $args[0]; 225 | } 226 | else if(isset($this->_keyFile)){ 227 | $this->_error[$this->_keyFile][] = $args[0]; 228 | } 229 | else{ 230 | $this->_error[$this->_key][] = $args[0]; 231 | } 232 | 233 | 234 | } 235 | function _custom($args){ 236 | call_user_func($args[0],$this); 237 | return $this; 238 | } 239 | function _rule($args){ 240 | 241 | call_user_func($args[0],$this); 242 | return $this; 243 | } 244 | 245 | public function __call($name, $arguments) { 246 | if($name=="custom"){ 247 | // print_r($arguments);exit; 248 | } 249 | 250 | $name = "_".strtolower($name); 251 | //echo $name."
"; 252 | //echo json_encode($arguments); 253 | $functionList = get_class_methods($this); 254 | if(in_array($name,$functionList)){ 255 | return $this->$name($arguments); 256 | } 257 | else{ 258 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 259 | } 260 | } 261 | 262 | public static function __callStatic($name, $arguments) { 263 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 264 | $name = "_".strtolower($name); 265 | $functionList = get_class_methods(self::$_instance); 266 | if(in_array($name,$functionList)){ 267 | return self::$_instance->$name($arguments); 268 | } 269 | else{ 270 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 271 | } 272 | } 273 | } 274 | 275 | ?> -------------------------------------------------------------------------------- /app.php: -------------------------------------------------------------------------------- 1 | routeList = []; 26 | $this->rootDir = getCwd()."/../"; 27 | $this->controllerDir = getcwd().'/../controller/'; 28 | //$this->db = new Database(); 29 | //echo "app "; 30 | $this->db = Database::Instance(); 31 | //echo " end-app "; 32 | 33 | } 34 | 35 | function replace($str,$replaceList){ 36 | foreach($replaceList as $val){ 37 | $str = str_replace($val,"",$str); 38 | } 39 | return $str; 40 | } 41 | 42 | function start(){ 43 | $url = $_SERVER['REQUEST_URI']; 44 | $url = explode("?",$url); 45 | $url = $url[0]; 46 | $replaceList = array( 47 | "/index.php/" 48 | ); 49 | $url = $this->replace($url,$replaceList); 50 | $this->route($url); 51 | } 52 | 53 | //function addroute($method,$url,$controllerPath,$functionName){ 54 | function addroute(){ 55 | //$method,$url,$controllerPath,$functionName 56 | $method = null; 57 | $url= null; 58 | $controllerPath= null; 59 | $functionName= null; 60 | 61 | $numArgs = func_num_args(); 62 | $args = func_get_args(); 63 | if($numArgs==3){ 64 | $method = $args[0]; 65 | $url= $args[1]; 66 | $controllerPath= null; 67 | $functionName=$args[2]; 68 | } 69 | else{ 70 | $method = $args[0]; 71 | $url= $args[1]; 72 | $controllerPath= $args[2]; 73 | $functionName=$args[3]; 74 | } 75 | $url = implode("",$this->routePrefix).$url; 76 | $url = $url[0]=="/"?$url:"/".$url; 77 | $url = str_replace("//","/",$url); 78 | //echo "/".$url.'
';exit; 79 | $this->routeList[$url][$method] =array( 80 | 'includeClass' => $controllerPath==""?false:true, 81 | 'method' => $method, 82 | 'controller' => $controllerPath, 83 | 'function' => $functionName, 84 | 'guard' => $this->guard 85 | ); 86 | } 87 | 88 | function routeGuard($guard,$function){ 89 | $this->guard = $guard; 90 | call_user_func($function,$this); 91 | // print_r($this->routePrefix);exit; 92 | // $this->routePrefix[$this->prefixIndex] = ""; 93 | $this->guard = ""; 94 | //print_r($this->routePrefix);exit; 95 | } 96 | 97 | function withGuard($guard){ 98 | $this->guard = $guard; 99 | return $this; 100 | } 101 | function routePrefix($prefix,$function,$defaultPrefix=""){ 102 | $this->prefixIndex = $this->prefixIndex +1; 103 | /* 104 | $this->routePrefix = $this->routePrefix."/".$prefix; 105 | var_dump($function);exit; 106 | call_user_func($function,$this,); 107 | $this->guard = ""; 108 | $this->routePrefix = ""; 109 | */ 110 | # echo "default -> ".$defaultPrefix."
"; 111 | # echo $prefix."
"; 112 | # echo $this->prefixIndex; 113 | // $this->routePrefix[$this->prefixIndex].$defaultPrefix."/".$prefix; 114 | $this->routePrefix[$this->prefixIndex] = $prefix[0]=="/"?$prefix:"/".$prefix; 115 | //var_dump($function);exit; 116 | //echo $defaultPrefix."1
"; 117 | //echo $this->routePrefix."1
"; 118 | call_user_func($function,$this,$prefix); 119 | //$this->guard = ""; 120 | 121 | unset($this->routePrefix[$this->prefixIndex]); 122 | $this->prefixIndex = $this->prefixIndex -1; 123 | } 124 | 125 | function mapping(){ 126 | 127 | } 128 | function goRoute($url,$method,$parameters){ 129 | //print_r($url); 130 | //print_r($parameters);exit; 131 | $route = $this->routeList[$url][$method]; 132 | if($route['guard']!=''){ 133 | $status = Auth::guard($route['guard'])->isLogin(); 134 | if(!$status){ 135 | $data = array( 136 | "status"=>403, 137 | "message" => "Access Denied for Incorrect Token" 138 | ); 139 | 140 | Response::outPut($data); 141 | return false; 142 | } 143 | } 144 | 145 | 146 | if( $route['includeClass']==true){ 147 | $nameSpace = "Controller\\".str_replace("/","\\",$route["controller"]); 148 | $classPath = $this->controllerDir.$route["controller"].".php"; 149 | 150 | if(file_exists($classPath)){ 151 | include $classPath; 152 | 153 | $functionName = $route['function']; 154 | $obj = new $nameSpace(); 155 | $request = new Request($this->db ); 156 | if(in_array($functionName,get_class_methods($obj))) 157 | { 158 | $result = $obj->$functionName($request,$parameters); 159 | Response::outPut($result); 160 | } 161 | else{ 162 | throw new ExceptionHandler(ExceptionHandler::FunctionNotFound($obj,$functionName,$route)); 163 | } 164 | 165 | } 166 | else{ 167 | echo "Controller not found -> $classPath"; 168 | } 169 | } 170 | else{ 171 | $function = $route['function']; 172 | call_user_func($function,$this); 173 | } 174 | } 175 | function route($url){ 176 | //echo ">>".$url;#exit; 177 | // var_dump($this->routeList);exit; 178 | //$db = Database::Instance(); 179 | 180 | // print_r($this->routeList);exit; 181 | $parameters = new \StdClass; 182 | $ttff = false; 183 | foreach ($this->routeList as $k=>$v) { 184 | //var_dump($k);exit; 185 | $original_urls = explode("/", $k); 186 | $request_urls = explode("/", $url); 187 | if(count($original_urls )==count($request_urls)){ 188 | 189 | 190 | $tf = true; 191 | foreach($original_urls as $ok=>$ov){ 192 | 193 | if($ok!=0){ 194 | $isParameter = strpos($original_urls[$ok],"{"); 195 | if ($isParameter === false) { 196 | if($original_urls[$ok] != $request_urls[$ok] ){ 197 | $tf = false; 198 | 199 | } 200 | } 201 | else{ 202 | $par = str_replace("{","",$original_urls[$ok]); 203 | $par = str_replace("}","",$par); 204 | //$parameters->$par = $request_urls[$ok]; 205 | //$this->db->conn->real_escape_string($arr) 206 | $parameters->$par = $this->db->conn->real_escape_string( $request_urls[$ok]); 207 | // echo $parameters->$par; 208 | } 209 | } 210 | 211 | } 212 | //var_dump($tf); 213 | if($tf){ 214 | 215 | $ttff = true; 216 | $this->goRoute($k,strtolower($_SERVER['REQUEST_METHOD']),$parameters); 217 | } 218 | else{ 219 | // $ttff = false; 220 | // throw new ExceptionHandler(ExceptionHandler::RouteNotFound($url,$this->routeList)); 221 | } 222 | 223 | } 224 | 225 | 226 | } 227 | //exit; 228 | //var_dump($ttff); 229 | if (!$ttff) { 230 | 231 | ///go rute 232 | 233 | throw new ExceptionHandler(ExceptionHandler::RouteNotFound($url,$this->routeList)); 234 | 235 | } 236 | } 237 | } 238 | 239 | $app = new App(); 240 | 241 | include 'Main.php'; 242 | include $providersDir.'ExceptionHandler.php'; 243 | 244 | $route = $app; 245 | 246 | include '../route/route.php'; 247 | $app = $route; 248 | 249 | include 'remoteDevice.php'; 250 | include 'auth.php'; 251 | include 'schema/table.php'; 252 | include $providersDir.'request.php'; 253 | include $providersDir.'validation.php'; 254 | include $providersDir.'response.php'; 255 | include $providersDir.'uploader.php'; 256 | include $providersDir.'S3.php'; 257 | include $providersDir.'Notifier.php'; 258 | include $providersDir.'Html.php'; 259 | include 'hash.php'; 260 | //print_r(Auth::guard); 261 | 262 | $dirs = scandir($app->rootDir."model"); 263 | foreach($dirs as $dir){ 264 | if(strpos($dir, ".php") !== false){ 265 | include $app->rootDir."model/".$dir; 266 | } 267 | } 268 | 269 | $dirs = scandir($app->rootDir."extenstions"); 270 | foreach($dirs as $dir){ 271 | if(strpos($dir, ".php") !== false){ 272 | include $app->rootDir."extenstions/".$dir; 273 | } 274 | } 275 | 276 | 277 | $app->start(); 278 | ?> -------------------------------------------------------------------------------- /schema/table.php: -------------------------------------------------------------------------------- 1 | database = new Database(); 42 | //echo "constructor"; 43 | // // //$this->database = Database::Instance(); 44 | //$this->defaultTableName = static::$tableName; 45 | //$this->staticPrimaryKey = static::$primaryKeys; 46 | if($db!==NULL){ 47 | 48 | $this->database = $db; 49 | } 50 | else{ 51 | //echo "-"; 52 | //echo "Request "; 53 | $this->database = Database::Instance(); 54 | } 55 | } 56 | public function __call($name, $arguments) { 57 | //echo "__call"; 58 | /* 59 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 60 | self::$_instance->database = Database::Instance(); 61 | $name = "_".$name; 62 | $functionList = get_class_methods($this); 63 | if(in_array($name,$functionList)){ 64 | self::$_instance->defaultTableName =static::$tableName; 65 | self::$_instance->staticPrimaryKey = static::$primaryKeys; 66 | return self::$_instance->$name( 67 | self::$_instance->defaultTableName, 68 | self::$_instance->staticPrimaryKey, 69 | $arguments); 70 | } 71 | else{ 72 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 73 | } 74 | */ 75 | $name = "_".$name; 76 | $functionList = get_class_methods($this); 77 | if(in_array($name,$functionList)){ 78 | //echo $this->defaultTableName;exit; 79 | if(!$this->defaultTableName){ 80 | $this->defaultTableName =static::$tableName; 81 | $this->staticPrimaryKey = static::$primaryKeys; 82 | } 83 | return $this->$name( 84 | $this->defaultTableName, 85 | $this->staticPrimaryKey, 86 | $arguments); 87 | } 88 | else{ 89 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 90 | } 91 | } 92 | public static function __callStatic($name, $arguments) { 93 | if(in_array($name,array('find','select'))){ 94 | //echo "haha"; 95 | self::$_instance = new self ; 96 | } 97 | else{ 98 | self::$_instance = (self::$_instance === null ? new self : self::$_instance); 99 | } 100 | 101 | //echo "table"; 102 | self::$_instance->database = Database::Instance(); 103 | $name = "_".$name; 104 | $functionList = get_class_methods(self::$_instance); 105 | if(in_array($name,$functionList)){ 106 | self::$_instance->defaultTableName =static::$tableName; 107 | self::$_instance->staticPrimaryKey = static::$primaryKeys; 108 | return self::$_instance->$name(self::$_instance->defaultTableName, 109 | self::$_instance->staticPrimaryKey, 110 | $arguments); 111 | } 112 | else{ 113 | throw new ExceptionHandler(ExceptionHandler::MethodNotFound($this,$name)); 114 | } 115 | } 116 | function _select($tableName,$primaryKeys,$args){ 117 | $this->whereCase = ""; 118 | $this->groupBy = ""; 119 | $this->defaultTableName = $tableName; 120 | $this->defaultColumnName = []; 121 | foreach($args as $arg){ 122 | $this->defaultColumnName[] = $arg; 123 | } 124 | return $this; 125 | } 126 | function where($queryString){ 127 | // $this->whereCase = ""; 128 | $this->whereCase = ($this->whereCase==""?" WHERE ".$queryString:$this->whereCase." AND ".$queryString); 129 | return $this; 130 | } 131 | function orderBy($queryString){ 132 | $this->orderBy = " ORDER BY ".$queryString; 133 | return $this; 134 | } 135 | function groupBy($queryString){ 136 | $this->groupBy = " GROUP BY ".$queryString; 137 | return $this; 138 | } 139 | function get(){ 140 | $select = $this->defaultColumnName ? implode(",",$this->defaultColumnName) : " * "; 141 | $oBy = $this->orderBy!==""?$this->orderBy:""; 142 | $cmdString = "SELECT ".$select." FROM ".$this->defaultTableName. 143 | ($this->whereCase!==""?$this->whereCase:""). 144 | ($this->groupBy!==""?($this->groupBy.$oBy):$oBy); 145 | $database = $this->database; 146 | //echo $cmdString;exit; 147 | $result = $database->query($cmdString); 148 | return $result->num_rows==0 ? false: $result; 149 | } 150 | function getAll(){ 151 | $select = $this->defaultColumnName ? implode(",",$this->defaultColumnName) : " * "; 152 | $oBy = $this->orderBy!==""?$this->orderBy:""; 153 | $cmdString = "SELECT ".$select." FROM ".$this->defaultTableName. 154 | ($this->whereCase!==""?$this->whereCase:""). 155 | ($this->groupBy!==""?($this->groupBy.$oBy):$oBy); 156 | $database = $this->database; 157 | //if($this->defaultTableName=="Branch"){ 158 | // echo $cmdString;//exit; 159 | // } 160 | 161 | $result = $database->query($cmdString); 162 | //return $result->num_rows==0 ? false: $result; 163 | if($result->num_rows==0){ 164 | return false; 165 | } 166 | else{ 167 | $data = []; 168 | while($row = $result->fetch_assoc()) { 169 | $data[] = $row; 170 | } 171 | return $data; 172 | } 173 | 174 | } 175 | function paginate($page_at,$row_count){ 176 | $select = $this->defaultColumnName ? implode(",",$this->defaultColumnName) : " * "; 177 | $oBy = $this->orderBy!==""?$this->orderBy:""; 178 | $cmdString = "SELECT ".$select." FROM ".$this->defaultTableName. 179 | ($this->whereCase!==""?$this->whereCase." AND deleted_at IS NULL":" WHERE deleted_at IS NULL"). 180 | ($this->groupBy!==""?($this->groupBy.$oBy):$oBy); 181 | $database = $this->database; 182 | $paginatecmdString = $cmdString . " LIMIT $page_at,$row_count"; 183 | // echo $cmdString;exit; 184 | 185 | $result = $database->query($cmdString); 186 | //return $result->num_rows==0 ? false: $result; 187 | if($result->num_rows==0){ 188 | return false; 189 | } 190 | else{ 191 | $totalRecords = $result->num_rows; 192 | $total_page = $totalRecords/ $row_count; 193 | 194 | $result = $database->query($cmdString); 195 | 196 | $from = $page_at*$row_count; 197 | $paginatecmdString = $cmdString . " LIMIT $from,$row_count"; 198 | //echo $paginatecmdString; 199 | 200 | $paginate = array( 201 | "page_at" => $page_at+1, 202 | "total_page" => ceil($total_page), 203 | "total_records_count" => $totalRecords 204 | ); 205 | 206 | $result = $database->query($paginatecmdString); 207 | 208 | $data = []; 209 | while($row = $result->fetch_assoc()) { 210 | $data[] = $row; 211 | } 212 | $returnData = new \stdClass(); 213 | $returnData->paginate = $paginate; 214 | $returnData->data = $data; 215 | return $returnData; 216 | } 217 | 218 | } 219 | function first(){ 220 | $database = $this->database; 221 | $select = $this->defaultColumnName ? implode(",",$this->defaultColumnName) : " * "; 222 | $oBy = $this->orderBy!==""?$this->orderBy:""; 223 | $cmdString = "SELECT ".$select." FROM ".$this->defaultTableName. 224 | ($this->whereCase!==""?$this->whereCase:""). 225 | ($this->groupBy!==""?($this->groupBy.$oBy):$oBy); 226 | $cmdString = $cmdString." LIMIT 0,1"; 227 | //echo $cmdString; 228 | //exit; 229 | $result = $database->query($cmdString); 230 | //var_dump($result);exit; 231 | if(!$result){ 232 | return false; 233 | } 234 | if($result->num_rows==1){ 235 | foreach($result as $key=>$value) 236 | { 237 | $obj = new \stdClass(); 238 | foreach($value as $k=>$v) 239 | { 240 | $obj->$k = $v; 241 | } 242 | 243 | return $obj; 244 | } 245 | } 246 | else{ 247 | return false; 248 | } 249 | //return $database->query($cmdString); 250 | } 251 | 252 | function retrieve(){ 253 | $database = $this->database; 254 | $select = $this->defaultColumnName ? implode(",",$this->defaultColumnName) : " * "; 255 | $oBy = $this->orderBy!==""?$this->orderBy:""; 256 | $cmdString = "SELECT ".$select." FROM ".$this->defaultTableName. 257 | ($this->whereCase!==""?$this->whereCase:""). 258 | ($this->groupBy!==""?($this->groupBy.$oBy):$oBy); 259 | $cmdString = $cmdString." LIMIT 0,1"; 260 | //echo $cmdString; 261 | //exit; 262 | $result = $database->query($cmdString); 263 | //var_dump($result);exit; 264 | if(!$result){ 265 | return false; 266 | } 267 | if($result->num_rows==1){ 268 | foreach($result as $key=>$value) 269 | { 270 | $record = new \stdClass(); 271 | foreach($value as $k=>$v) 272 | { 273 | $record->$k = $v; 274 | } 275 | $obj = $this; 276 | $obj->_fields = $record; 277 | return $this; 278 | // return $obj; 279 | } 280 | } 281 | else{ 282 | return false; 283 | } 284 | //return $database->query($cmdString); 285 | } 286 | 287 | function _delete($tableName,$primaryKeys,$args){ 288 | $database = $this->database; 289 | $cmdString = "UPDATE $tableName SET deleted_at=". "'".date("yy-m-d h:i:s")."', ". 290 | "updated_at='".date("yy-m-d h:i:s")."' WHERE ".$args[0]; 291 | //echo $cmdString; 292 | $result = $database->query($cmdString); 293 | if ($result === TRUE) { 294 | return true; 295 | } else { 296 | echo $database->conn->error; 297 | exit; 298 | } 299 | } 300 | function _update($tableName,$primaryKeys,$args){ 301 | #echo "hello";exit; 302 | // print_r($primaryKeys);#exit; 303 | //print_r(static::$tableName); 304 | $cmdString = "UPDATE $tableName SET "; 305 | $cmd = []; 306 | foreach (get_object_vars($this) as $prop_name => $prop_value) { 307 | if(!in_array($prop_name,$this->defaultPros)){ 308 | //echo " x ".$prop_name.":".$prop_value." y "; 309 | if($primaryKeys[0]==$prop_name){ 310 | //print_r($primaryKeys[0]);print_r($prop_value); 311 | //$cmd[] = $prop_name."=".(is_int($prop_value)?$prop_value:($prop_value==null?"NULL":"'".$prop_value."'"))." "; 312 | //if($prop_name!='_fields'){ 313 | $cmd[] = $prop_name."=".(int)$prop_value." "; 314 | //} 315 | } 316 | else{ 317 | if($prop_name=="updated_at"){ 318 | $prop_value =date("yy-m-d h:i:s"); 319 | } 320 | // if($prop_name!='_fields'){ 321 | 322 | $cmd[] = "`".$prop_name."`=".(is_int($prop_value)?$prop_value:($prop_value==null?"NULL":"'".$prop_value."'"))." "; 323 | //} 324 | 325 | /* if(is_a($this->$prop_name,'stdClass')){ 326 | echo 'x'; 327 | } 328 | else{ 329 | $cmd[] = "`".$prop_name."`=".(is_int($prop_value)?$prop_value:($prop_value==null?"NULL":"'".$prop_value."'"))." "; 330 | } 331 | */ 332 | 333 | 334 | } 335 | } 336 | }; 337 | // print_r($cmd);exit; 338 | //print_r($primaryKeys);exit; 339 | $key = $primaryKeys[0]; 340 | //var_dump($this); 341 | //echo $key;exit; 342 | //echo $this->$key;exit; 343 | // $cmdString = $cmdString. implode(",",$cmd) ." WHERE ".$primaryKeys[0]."=".(is_int($this->$key)?$this->$key:"'".$this->$key."'").";"; 344 | $cmdString = $cmdString. implode(",",$cmd) ." WHERE ".$primaryKeys[0]."=".$this->$key.";"; 345 | $cmdString = str_replace("\\", "\\"."\\", $cmdString); 346 | // echo $cmdString;exit; 347 | $database = $this->database; 348 | $result = $database->query($cmdString); 349 | if ($result === TRUE) { 350 | //$this->id = $database->conn->insert_id; 351 | } else { 352 | echo $database->conn->error; 353 | exit; 354 | } 355 | } 356 | function _find($tableName,$primaryKeys,$args){ 357 | //echo $tableName;exit; 358 | #print_r($primaryKeys);exit; 359 | # print_r($args);exit; 360 | $id = $args[0]; 361 | #echo $id;exit; 362 | $columnId = $primaryKeys[0]; 363 | #echo $columnId;exit; 364 | //echo static::$tableName;exit; 365 | $this->defaultTableName = $tableName; 366 | $cmdString = "SELECT * FROM ".$tableName ." WHERE $columnId=".(is_int($id)?$id:"'".$id."'"). " AND deleted_at IS NULL"; 367 | //$database = new Database(); 368 | #echo $cmdString ;exit; 369 | $database = self::$_instance->database; 370 | $result = $database->query($cmdString); 371 | //echo $result['num_rows'];exit; 372 | $hasRecord = false; 373 | if($result){ 374 | foreach($result as $row){ 375 | $hasRecord = true; 376 | foreach($row as $key=>$value){ 377 | $hiddenColumns = static::$hiddenColumns; 378 | if(!in_array($key,$hiddenColumns)){ 379 | if($key!="primaryKeys"){ 380 | self::$_instance->$key = $value; 381 | } 382 | } 383 | } 384 | } 385 | } 386 | else{ 387 | return false; 388 | } 389 | return $hasRecord ? self::$_instance : false; 390 | } 391 | function _save($tableName,$primaryKeys){ 392 | //echo $tableName;exit; 393 | //echo $tableName;exit; 394 | //print_r($tableName); 395 | $columns = []; 396 | $data = []; 397 | //var_dump(get_object_vars($this)); 398 | foreach (get_object_vars($this) as $prop_name => $prop_value) { 399 | if(!in_array($prop_name,$this->defaultPros)){ 400 | $columns[] = "`".$prop_name."`"; 401 | if($prop_value===null){ 402 | $data[] = "NULL"; 403 | } 404 | else{ 405 | $data[] = "'".$prop_value."'"; 406 | } 407 | } 408 | } 409 | if($this->softDelete){ 410 | if (!in_array("`created_at`",$columns)) 411 | { 412 | $columns[] = "`created_at`"; 413 | $data[] = "'".date("yy-m-d h:i:s")."'"; 414 | } 415 | if (!in_array("`updated_at`",$columns)) 416 | { 417 | $columns[] = "`updated_at`"; 418 | $data[] = "'".date("yy-m-d h:i:s")."'"; 419 | } 420 | if (!in_array("`id`",$columns)) 421 | { 422 | if(count($primaryKeys)==0){ 423 | $columns[] = "`id`"; 424 | $data[] = "NULL"; 425 | } 426 | } 427 | } 428 | //echo "hello"; 429 | //$tableName = static::$tableName; 430 | // echo implode(",",$columns);exit; 431 | $cmdString = "INSERT INTO `$tableName` (".implode(",",$columns).") VALUES " ."(".implode(",",$data).");"; 432 | $cmdString = str_replace("\\", "\\"."\\", $cmdString); 433 | // echo $cmdString; exit; 434 | $database = $this->database; 435 | $result = $database->query($cmdString); 436 | if ($result === TRUE) { 437 | $this->id = $database->conn->insert_id; 438 | } else { 439 | echo $database->conn->error; 440 | exit; 441 | } 442 | } 443 | } 444 | ?> 445 | -------------------------------------------------------------------------------- /providers/S3.php: -------------------------------------------------------------------------------- 1 | $host, 'type' => $type, 'user' => $user, 'pass' => $pass); 316 | } 317 | 318 | 319 | /** 320 | * Set the error mode to exceptions 321 | * 322 | * @param boolean $enabled Enable exceptions 323 | * @return void 324 | */ 325 | public static function setExceptions($enabled = true) 326 | { 327 | self::$useExceptions = $enabled; 328 | } 329 | 330 | 331 | /** 332 | * Set AWS time correction offset (use carefully) 333 | * 334 | * This can be used when an inaccurate system time is generating 335 | * invalid request signatures. It should only be used as a last 336 | * resort when the system time cannot be changed. 337 | * 338 | * @param string $offset Time offset (set to zero to use AWS server time) 339 | * @return void 340 | */ 341 | public static function setTimeCorrectionOffset($offset = 0) 342 | { 343 | if ($offset == 0) 344 | { 345 | $rest = new S3Request('HEAD'); 346 | $rest = $rest->getResponse(); 347 | $awstime = $rest->headers['date']; 348 | $systime = time(); 349 | $offset = $systime > $awstime ? -($systime - $awstime) : ($awstime - $systime); 350 | } 351 | self::$__timeOffset = $offset; 352 | } 353 | 354 | 355 | /** 356 | * Set signing key 357 | * 358 | * @param string $keyPairId AWS Key Pair ID 359 | * @param string $signingKey Private Key 360 | * @param boolean $isFile Load private key from file, set to false to load string 361 | * @return boolean 362 | */ 363 | public static function setSigningKey($keyPairId, $signingKey, $isFile = true) 364 | { 365 | self::$__signingKeyPairId = $keyPairId; 366 | if ((self::$__signingKeyResource = openssl_pkey_get_private($isFile ? 367 | file_get_contents($signingKey) : $signingKey)) !== false) return true; 368 | self::__triggerError('S3::setSigningKey(): Unable to open load private key: '.$signingKey, __FILE__, __LINE__); 369 | return false; 370 | } 371 | 372 | 373 | /** 374 | * Set Signature Version 375 | * 376 | * @param string $version of signature ('v4' or 'v2') 377 | * @return void 378 | */ 379 | public static function setSignatureVersion($version = 'v2') 380 | { 381 | self::$signVer = $version; 382 | } 383 | 384 | 385 | /** 386 | * Free signing key from memory, MUST be called if you are using setSigningKey() 387 | * 388 | * @return void 389 | */ 390 | public static function freeSigningKey() 391 | { 392 | if (self::$__signingKeyResource !== false) 393 | openssl_free_key(self::$__signingKeyResource); 394 | } 395 | 396 | /** 397 | * Set progress function 398 | * 399 | * @param function $func Progress function 400 | * @return void 401 | */ 402 | public static function setProgressFunction($func = null) 403 | { 404 | self::$progressFunction = $func; 405 | } 406 | 407 | 408 | /** 409 | * Internal error handler 410 | * 411 | * @internal Internal error handler 412 | * @param string $message Error message 413 | * @param string $file Filename 414 | * @param integer $line Line number 415 | * @param integer $code Error code 416 | * @return void 417 | */ 418 | private static function __triggerError($message, $file, $line, $code = 0) 419 | { 420 | if (self::$useExceptions) 421 | throw new S3Exception($message, $file, $line, $code); 422 | else 423 | trigger_error($message, E_USER_WARNING); 424 | } 425 | 426 | 427 | /** 428 | * Get a list of buckets 429 | * 430 | * @param boolean $detailed Returns detailed bucket list when true 431 | * @return array | false 432 | */ 433 | public static function listBuckets($detailed = false) 434 | { 435 | $rest = new S3Request('GET', '', '', self::$endpoint); 436 | $rest = $rest->getResponse(); 437 | if ($rest->error === false && $rest->code !== 200) 438 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 439 | if ($rest->error !== false) 440 | { 441 | self::__triggerError(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], 442 | $rest->error['message']), __FILE__, __LINE__); 443 | return false; 444 | } 445 | $results = array(); 446 | if (!isset($rest->body->Buckets)) return $results; 447 | 448 | if ($detailed) 449 | { 450 | if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) 451 | $results['owner'] = array( 452 | 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName 453 | ); 454 | $results['buckets'] = array(); 455 | foreach ($rest->body->Buckets->Bucket as $b) 456 | $results['buckets'][] = array( 457 | 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) 458 | ); 459 | } else 460 | foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; 461 | 462 | return $results; 463 | } 464 | 465 | 466 | /** 467 | * Get contents for a bucket 468 | * 469 | * If maxKeys is null this method will loop through truncated result sets 470 | * 471 | * @param string $bucket Bucket name 472 | * @param string $prefix Prefix 473 | * @param string $marker Marker (last file listed) 474 | * @param string $maxKeys Max keys (maximum number of keys to return) 475 | * @param string $delimiter Delimiter 476 | * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes 477 | * @return array | false 478 | */ 479 | public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) 480 | { 481 | $rest = new S3Request('GET', $bucket, '', self::$endpoint); 482 | if ($maxKeys == 0) $maxKeys = null; 483 | if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); 484 | if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); 485 | if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); 486 | if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); 487 | else if (!empty(self::$defDelimiter)) $rest->setParameter('delimiter', self::$defDelimiter); 488 | $response = $rest->getResponse(); 489 | if ($response->error === false && $response->code !== 200) 490 | $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); 491 | if ($response->error !== false) 492 | { 493 | self::__triggerError(sprintf("S3::getBucket(): [%s] %s", 494 | $response->error['code'], $response->error['message']), __FILE__, __LINE__); 495 | return false; 496 | } 497 | 498 | $results = array(); 499 | 500 | $nextMarker = null; 501 | if (isset($response->body, $response->body->Contents)) 502 | foreach ($response->body->Contents as $c) 503 | { 504 | $results[(string)$c->Key] = array( 505 | 'name' => (string)$c->Key, 506 | 'time' => strtotime((string)$c->LastModified), 507 | 'size' => (int)$c->Size, 508 | 'hash' => substr((string)$c->ETag, 1, -1) 509 | ); 510 | $nextMarker = (string)$c->Key; 511 | } 512 | 513 | if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) 514 | foreach ($response->body->CommonPrefixes as $c) 515 | $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); 516 | 517 | if (isset($response->body, $response->body->IsTruncated) && 518 | (string)$response->body->IsTruncated == 'false') return $results; 519 | 520 | if (isset($response->body, $response->body->NextMarker)) 521 | $nextMarker = (string)$response->body->NextMarker; 522 | 523 | // Loop through truncated results if maxKeys isn't specified 524 | if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') 525 | do 526 | { 527 | $rest = new S3Request('GET', $bucket, '', self::$endpoint); 528 | if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); 529 | $rest->setParameter('marker', $nextMarker); 530 | if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); 531 | 532 | if (($response = $rest->getResponse()) == false || $response->code !== 200) break; 533 | 534 | if (isset($response->body, $response->body->Contents)) 535 | foreach ($response->body->Contents as $c) 536 | { 537 | $results[(string)$c->Key] = array( 538 | 'name' => (string)$c->Key, 539 | 'time' => strtotime((string)$c->LastModified), 540 | 'size' => (int)$c->Size, 541 | 'hash' => substr((string)$c->ETag, 1, -1) 542 | ); 543 | $nextMarker = (string)$c->Key; 544 | } 545 | 546 | if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) 547 | foreach ($response->body->CommonPrefixes as $c) 548 | $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); 549 | 550 | if (isset($response->body, $response->body->NextMarker)) 551 | $nextMarker = (string)$response->body->NextMarker; 552 | 553 | } while ($response !== false && (string)$response->body->IsTruncated == 'true'); 554 | 555 | return $results; 556 | } 557 | 558 | 559 | /** 560 | * Put a bucket 561 | * 562 | * @param string $bucket Bucket name 563 | * @param constant $acl ACL flag 564 | * @param string $location Set as "EU" to create buckets hosted in Europe 565 | * @return boolean 566 | */ 567 | public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) 568 | { 569 | $rest = new S3Request('PUT', $bucket, '', self::$endpoint); 570 | $rest->setAmzHeader('x-amz-acl', $acl); 571 | 572 | if ($location === false) $location = self::getRegion(); 573 | 574 | if ($location !== false && $location !== "us-east-1") 575 | { 576 | $dom = new DOMDocument; 577 | $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); 578 | $locationConstraint = $dom->createElement('LocationConstraint', $location); 579 | $createBucketConfiguration->appendChild($locationConstraint); 580 | $dom->appendChild($createBucketConfiguration); 581 | $rest->data = $dom->saveXML(); 582 | $rest->size = strlen($rest->data); 583 | $rest->setHeader('Content-Type', 'application/xml'); 584 | } 585 | $rest = $rest->getResponse(); 586 | 587 | if ($rest->error === false && $rest->code !== 200) 588 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 589 | if ($rest->error !== false) 590 | { 591 | self::__triggerError(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", 592 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 593 | return false; 594 | } 595 | return true; 596 | } 597 | 598 | 599 | /** 600 | * Delete an empty bucket 601 | * 602 | * @param string $bucket Bucket name 603 | * @return boolean 604 | */ 605 | public static function deleteBucket($bucket) 606 | { 607 | $rest = new S3Request('DELETE', $bucket, '', self::$endpoint); 608 | $rest = $rest->getResponse(); 609 | if ($rest->error === false && $rest->code !== 204) 610 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 611 | if ($rest->error !== false) 612 | { 613 | self::__triggerError(sprintf("S3::deleteBucket({$bucket}): [%s] %s", 614 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 615 | return false; 616 | } 617 | return true; 618 | } 619 | 620 | 621 | /** 622 | * Create input info array for putObject() 623 | * 624 | * @param string $file Input file 625 | * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) 626 | * @return array | false 627 | */ 628 | public static function inputFile($file, $md5sum = true) 629 | { 630 | if (!file_exists($file) || !is_file($file) || !is_readable($file)) 631 | { 632 | self::__triggerError('S3::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__); 633 | return false; 634 | } 635 | clearstatcache(false, $file); 636 | return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ? 637 | (is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : '', 'sha256sum' => hash_file('sha256', $file)); 638 | } 639 | 640 | 641 | /** 642 | * Create input array info for putObject() with a resource 643 | * 644 | * @param string $resource Input resource to read from 645 | * @param integer $bufferSize Input byte size 646 | * @param string $md5sum MD5 hash to send (optional) 647 | * @return array | false 648 | */ 649 | public static function inputResource(&$resource, $bufferSize = false, $md5sum = '') 650 | { 651 | if (!is_resource($resource) || (int)$bufferSize < 0) 652 | { 653 | self::__triggerError('S3::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__); 654 | return false; 655 | } 656 | 657 | // Try to figure out the bytesize 658 | if ($bufferSize === false) 659 | { 660 | if (fseek($resource, 0, SEEK_END) < 0 || ($bufferSize = ftell($resource)) === false) 661 | { 662 | self::__triggerError('S3::inputResource(): Unable to obtain resource size', __FILE__, __LINE__); 663 | return false; 664 | } 665 | fseek($resource, 0); 666 | } 667 | 668 | $input = array('size' => $bufferSize, 'md5sum' => $md5sum); 669 | $input['fp'] =& $resource; 670 | return $input; 671 | } 672 | 673 | 674 | /** 675 | * Put an object 676 | * 677 | * @param mixed $input Input data 678 | * @param string $bucket Bucket name 679 | * @param string $uri Object URI 680 | * @param constant $acl ACL constant 681 | * @param array $metaHeaders Array of x-amz-meta-* headers 682 | * @param array $requestHeaders Array of request headers or content type as a string 683 | * @param constant $storageClass Storage class constant 684 | * @param constant $serverSideEncryption Server-side encryption 685 | * @return boolean 686 | */ 687 | public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD, $serverSideEncryption = self::SSE_NONE) 688 | { 689 | if ($input === false) return false; 690 | $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint); 691 | 692 | if (!is_array($input)) $input = array( 693 | 'data' => $input, 'size' => strlen($input), 694 | 'md5sum' => base64_encode(md5($input, true)), 695 | 'sha256sum' => hash('sha256', $input) 696 | ); 697 | 698 | // Data 699 | if (isset($input['fp'])) 700 | $rest->fp =& $input['fp']; 701 | elseif (isset($input['file'])) 702 | $rest->fp = @fopen($input['file'], 'rb'); 703 | elseif (isset($input['data'])) 704 | $rest->data = $input['data']; 705 | 706 | // Content-Length (required) 707 | if (isset($input['size']) && $input['size'] >= 0) 708 | $rest->size = $input['size']; 709 | else { 710 | if (isset($input['file'])) { 711 | clearstatcache(false, $input['file']); 712 | $rest->size = filesize($input['file']); 713 | } 714 | elseif (isset($input['data'])) 715 | $rest->size = strlen($input['data']); 716 | } 717 | 718 | // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) 719 | if (is_array($requestHeaders)) 720 | foreach ($requestHeaders as $h => $v) 721 | strpos($h, 'x-amz-') === 0 ? $rest->setAmzHeader($h, $v) : $rest->setHeader($h, $v); 722 | elseif (is_string($requestHeaders)) // Support for legacy contentType parameter 723 | $input['type'] = $requestHeaders; 724 | 725 | // Content-Type 726 | if (!isset($input['type'])) 727 | { 728 | if (isset($requestHeaders['Content-Type'])) 729 | $input['type'] =& $requestHeaders['Content-Type']; 730 | elseif (isset($input['file'])) 731 | $input['type'] = self::__getMIMEType($input['file']); 732 | else 733 | $input['type'] = 'application/octet-stream'; 734 | } 735 | 736 | if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class 737 | $rest->setAmzHeader('x-amz-storage-class', $storageClass); 738 | 739 | if ($serverSideEncryption !== self::SSE_NONE) // Server-side encryption 740 | $rest->setAmzHeader('x-amz-server-side-encryption', $serverSideEncryption); 741 | 742 | // We need to post with Content-Length and Content-Type, MD5 is optional 743 | if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) 744 | { 745 | $rest->setHeader('Content-Type', $input['type']); 746 | if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); 747 | 748 | if (isset($input['sha256sum'])) $rest->setAmzHeader('x-amz-content-sha256', $input['sha256sum']); 749 | 750 | $rest->setAmzHeader('x-amz-acl', $acl); 751 | foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); 752 | $rest->getResponse(); 753 | } else 754 | $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); 755 | 756 | if ($rest->response->error === false && $rest->response->code !== 200) 757 | $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); 758 | if ($rest->response->error !== false) 759 | { 760 | self::__triggerError(sprintf("S3::putObject(): [%s] %s", 761 | $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); 762 | return false; 763 | } 764 | return true; 765 | } 766 | 767 | 768 | /** 769 | * Put an object from a file (legacy function) 770 | * 771 | * @param string $file Input file path 772 | * @param string $bucket Bucket name 773 | * @param string $uri Object URI 774 | * @param constant $acl ACL constant 775 | * @param array $metaHeaders Array of x-amz-meta-* headers 776 | * @param string $contentType Content type 777 | * @return boolean 778 | */ 779 | public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType) 780 | { 781 | return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); 782 | } 783 | 784 | 785 | /** 786 | * Put an object from a string (legacy function) 787 | * 788 | * @param string $string Input data 789 | * @param string $bucket Bucket name 790 | * @param string $uri Object URI 791 | * @param constant $acl ACL constant 792 | * @param array $metaHeaders Array of x-amz-meta-* headers 793 | * @param string $contentType Content type 794 | * @return boolean 795 | */ 796 | public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') 797 | { 798 | return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); 799 | } 800 | 801 | 802 | /** 803 | * Get an object 804 | * 805 | * @param string $bucket Bucket name 806 | * @param string $uri Object URI 807 | * @param mixed $saveTo Filename or resource to write to 808 | * @return mixed 809 | */ 810 | public static function getObject($bucket, $uri, $saveTo = false) 811 | { 812 | $rest = new S3Request('GET', $bucket, $uri, self::$endpoint); 813 | if ($saveTo !== false) 814 | { 815 | if (is_resource($saveTo)) 816 | $rest->fp =& $saveTo; 817 | else 818 | if (($rest->fp = @fopen($saveTo, 'wb')) !== false) 819 | $rest->file = realpath($saveTo); 820 | else 821 | $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); 822 | } 823 | if ($rest->response->error === false) $rest->getResponse(); 824 | 825 | if ($rest->response->error === false && $rest->response->code !== 200) 826 | $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); 827 | if ($rest->response->error !== false) 828 | { 829 | self::__triggerError(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", 830 | $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); 831 | return false; 832 | } 833 | return $rest->response; 834 | } 835 | 836 | 837 | /** 838 | * Get object information 839 | * 840 | * @param string $bucket Bucket name 841 | * @param string $uri Object URI 842 | * @param boolean $returnInfo Return response information 843 | * @return mixed | false 844 | */ 845 | public static function getObjectInfo($bucket, $uri, $returnInfo = true) 846 | { 847 | $rest = new S3Request('HEAD', $bucket, $uri, self::$endpoint); 848 | $rest = $rest->getResponse(); 849 | if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) 850 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 851 | if ($rest->error !== false) 852 | { 853 | self::__triggerError(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", 854 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 855 | return false; 856 | } 857 | return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; 858 | } 859 | 860 | 861 | /** 862 | * Copy an object 863 | * 864 | * @param string $srcBucket Source bucket name 865 | * @param string $srcUri Source object URI 866 | * @param string $bucket Destination bucket name 867 | * @param string $uri Destination object URI 868 | * @param constant $acl ACL constant 869 | * @param array $metaHeaders Optional array of x-amz-meta-* headers 870 | * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) 871 | * @param constant $storageClass Storage class constant 872 | * @return mixed | false 873 | */ 874 | public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) 875 | { 876 | $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint); 877 | $rest->setHeader('Content-Length', 0); 878 | foreach ($requestHeaders as $h => $v) 879 | strpos($h, 'x-amz-') === 0 ? $rest->setAmzHeader($h, $v) : $rest->setHeader($h, $v); 880 | foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); 881 | if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class 882 | $rest->setAmzHeader('x-amz-storage-class', $storageClass); 883 | $rest->setAmzHeader('x-amz-acl', $acl); 884 | $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri))); 885 | if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) 886 | $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); 887 | 888 | $rest = $rest->getResponse(); 889 | if ($rest->error === false && $rest->code !== 200) 890 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 891 | if ($rest->error !== false) 892 | { 893 | self::__triggerError(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", 894 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 895 | return false; 896 | } 897 | return isset($rest->body->LastModified, $rest->body->ETag) ? array( 898 | 'time' => strtotime((string)$rest->body->LastModified), 899 | 'hash' => substr((string)$rest->body->ETag, 1, -1) 900 | ) : false; 901 | } 902 | 903 | 904 | /** 905 | * Set up a bucket redirection 906 | * 907 | * @param string $bucket Bucket name 908 | * @param string $location Target host name 909 | * @return boolean 910 | */ 911 | public static function setBucketRedirect($bucket = NULL, $location = NULL) 912 | { 913 | $rest = new S3Request('PUT', $bucket, '', self::$endpoint); 914 | 915 | if( empty($bucket) || empty($location) ) { 916 | self::__triggerError("S3::setBucketRedirect({$bucket}, {$location}): Empty parameter.", __FILE__, __LINE__); 917 | return false; 918 | } 919 | 920 | $dom = new DOMDocument; 921 | $websiteConfiguration = $dom->createElement('WebsiteConfiguration'); 922 | $redirectAllRequestsTo = $dom->createElement('RedirectAllRequestsTo'); 923 | $hostName = $dom->createElement('HostName', $location); 924 | $redirectAllRequestsTo->appendChild($hostName); 925 | $websiteConfiguration->appendChild($redirectAllRequestsTo); 926 | $dom->appendChild($websiteConfiguration); 927 | $rest->setParameter('website', null); 928 | $rest->data = $dom->saveXML(); 929 | $rest->size = strlen($rest->data); 930 | $rest->setHeader('Content-Type', 'application/xml'); 931 | $rest = $rest->getResponse(); 932 | 933 | if ($rest->error === false && $rest->code !== 200) 934 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 935 | if ($rest->error !== false) 936 | { 937 | self::__triggerError(sprintf("S3::setBucketRedirect({$bucket}, {$location}): [%s] %s", 938 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 939 | return false; 940 | } 941 | return true; 942 | } 943 | 944 | 945 | /** 946 | * Set logging for a bucket 947 | * 948 | * @param string $bucket Bucket name 949 | * @param string $targetBucket Target bucket (where logs are stored) 950 | * @param string $targetPrefix Log prefix (e,g; domain.com-) 951 | * @return boolean 952 | */ 953 | public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) 954 | { 955 | // The S3 log delivery group has to be added to the target bucket's ACP 956 | if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) 957 | { 958 | // Only add permissions to the target bucket when they do not exist 959 | $aclWriteSet = false; 960 | $aclReadSet = false; 961 | foreach ($acp['acl'] as $acl) 962 | if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') 963 | { 964 | if ($acl['permission'] == 'WRITE') $aclWriteSet = true; 965 | elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; 966 | } 967 | if (!$aclWriteSet) $acp['acl'][] = array( 968 | 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' 969 | ); 970 | if (!$aclReadSet) $acp['acl'][] = array( 971 | 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' 972 | ); 973 | if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp); 974 | } 975 | 976 | $dom = new DOMDocument; 977 | $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); 978 | $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); 979 | if ($targetBucket !== null) 980 | { 981 | if ($targetPrefix == null) $targetPrefix = $bucket . '-'; 982 | $loggingEnabled = $dom->createElement('LoggingEnabled'); 983 | $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); 984 | $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); 985 | // TODO: Add TargetGrants? 986 | $bucketLoggingStatus->appendChild($loggingEnabled); 987 | } 988 | $dom->appendChild($bucketLoggingStatus); 989 | 990 | $rest = new S3Request('PUT', $bucket, '', self::$endpoint); 991 | $rest->setParameter('logging', null); 992 | $rest->data = $dom->saveXML(); 993 | $rest->size = strlen($rest->data); 994 | $rest->setHeader('Content-Type', 'application/xml'); 995 | $rest = $rest->getResponse(); 996 | if ($rest->error === false && $rest->code !== 200) 997 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 998 | if ($rest->error !== false) 999 | { 1000 | self::__triggerError(sprintf("S3::setBucketLogging({$bucket}, {$targetBucket}): [%s] %s", 1001 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1002 | return false; 1003 | } 1004 | return true; 1005 | } 1006 | 1007 | 1008 | /** 1009 | * Get logging status for a bucket 1010 | * 1011 | * This will return false if logging is not enabled. 1012 | * Note: To enable logging, you also need to grant write access to the log group 1013 | * 1014 | * @param string $bucket Bucket name 1015 | * @return array | false 1016 | */ 1017 | public static function getBucketLogging($bucket) 1018 | { 1019 | $rest = new S3Request('GET', $bucket, '', self::$endpoint); 1020 | $rest->setParameter('logging', null); 1021 | $rest = $rest->getResponse(); 1022 | if ($rest->error === false && $rest->code !== 200) 1023 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1024 | if ($rest->error !== false) 1025 | { 1026 | self::__triggerError(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", 1027 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1028 | return false; 1029 | } 1030 | if (!isset($rest->body->LoggingEnabled)) return false; // No logging 1031 | return array( 1032 | 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, 1033 | 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, 1034 | ); 1035 | } 1036 | 1037 | 1038 | /** 1039 | * Disable bucket logging 1040 | * 1041 | * @param string $bucket Bucket name 1042 | * @return boolean 1043 | */ 1044 | public static function disableBucketLogging($bucket) 1045 | { 1046 | return self::setBucketLogging($bucket, null); 1047 | } 1048 | 1049 | 1050 | /** 1051 | * Get a bucket's location 1052 | * 1053 | * @param string $bucket Bucket name 1054 | * @return string | false 1055 | */ 1056 | public static function getBucketLocation($bucket) 1057 | { 1058 | $rest = new S3Request('GET', $bucket, '', self::$endpoint); 1059 | $rest->setParameter('location', null); 1060 | $rest = $rest->getResponse(); 1061 | if ($rest->error === false && $rest->code !== 200) 1062 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1063 | if ($rest->error !== false) 1064 | { 1065 | self::__triggerError(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", 1066 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1067 | return false; 1068 | } 1069 | return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; 1070 | } 1071 | 1072 | 1073 | /** 1074 | * Set object or bucket Access Control Policy 1075 | * 1076 | * @param string $bucket Bucket name 1077 | * @param string $uri Object URI 1078 | * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) 1079 | * @return boolean 1080 | */ 1081 | public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) 1082 | { 1083 | $dom = new DOMDocument; 1084 | $dom->formatOutput = true; 1085 | $accessControlPolicy = $dom->createElement('AccessControlPolicy'); 1086 | $accessControlList = $dom->createElement('AccessControlList'); 1087 | 1088 | // It seems the owner has to be passed along too 1089 | $owner = $dom->createElement('Owner'); 1090 | $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); 1091 | $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); 1092 | $accessControlPolicy->appendChild($owner); 1093 | 1094 | foreach ($acp['acl'] as $g) 1095 | { 1096 | $grant = $dom->createElement('Grant'); 1097 | $grantee = $dom->createElement('Grantee'); 1098 | $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); 1099 | if (isset($g['id'])) 1100 | { // CanonicalUser (DisplayName is omitted) 1101 | $grantee->setAttribute('xsi:type', 'CanonicalUser'); 1102 | $grantee->appendChild($dom->createElement('ID', $g['id'])); 1103 | } 1104 | elseif (isset($g['email'])) 1105 | { // AmazonCustomerByEmail 1106 | $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); 1107 | $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); 1108 | } 1109 | elseif ($g['type'] == 'Group') 1110 | { // Group 1111 | $grantee->setAttribute('xsi:type', 'Group'); 1112 | $grantee->appendChild($dom->createElement('URI', $g['uri'])); 1113 | } 1114 | $grant->appendChild($grantee); 1115 | $grant->appendChild($dom->createElement('Permission', $g['permission'])); 1116 | $accessControlList->appendChild($grant); 1117 | } 1118 | 1119 | $accessControlPolicy->appendChild($accessControlList); 1120 | $dom->appendChild($accessControlPolicy); 1121 | 1122 | $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint); 1123 | $rest->setParameter('acl', null); 1124 | $rest->data = $dom->saveXML(); 1125 | $rest->size = strlen($rest->data); 1126 | $rest->setHeader('Content-Type', 'application/xml'); 1127 | $rest = $rest->getResponse(); 1128 | if ($rest->error === false && $rest->code !== 200) 1129 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1130 | if ($rest->error !== false) 1131 | { 1132 | self::__triggerError(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", 1133 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1134 | return false; 1135 | } 1136 | return true; 1137 | } 1138 | 1139 | 1140 | /** 1141 | * Get object or bucket Access Control Policy 1142 | * 1143 | * @param string $bucket Bucket name 1144 | * @param string $uri Object URI 1145 | * @return mixed | false 1146 | */ 1147 | public static function getAccessControlPolicy($bucket, $uri = '') 1148 | { 1149 | $rest = new S3Request('GET', $bucket, $uri, self::$endpoint); 1150 | $rest->setParameter('acl', null); 1151 | $rest = $rest->getResponse(); 1152 | if ($rest->error === false && $rest->code !== 200) 1153 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1154 | if ($rest->error !== false) 1155 | { 1156 | self::__triggerError(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", 1157 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1158 | return false; 1159 | } 1160 | 1161 | $acp = array(); 1162 | if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) 1163 | $acp['owner'] = array( 1164 | 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName 1165 | ); 1166 | 1167 | if (isset($rest->body->AccessControlList)) 1168 | { 1169 | $acp['acl'] = array(); 1170 | foreach ($rest->body->AccessControlList->Grant as $grant) 1171 | { 1172 | foreach ($grant->Grantee as $grantee) 1173 | { 1174 | if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser 1175 | $acp['acl'][] = array( 1176 | 'type' => 'CanonicalUser', 1177 | 'id' => (string)$grantee->ID, 1178 | 'name' => (string)$grantee->DisplayName, 1179 | 'permission' => (string)$grant->Permission 1180 | ); 1181 | elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail 1182 | $acp['acl'][] = array( 1183 | 'type' => 'AmazonCustomerByEmail', 1184 | 'email' => (string)$grantee->EmailAddress, 1185 | 'permission' => (string)$grant->Permission 1186 | ); 1187 | elseif (isset($grantee->URI)) // Group 1188 | $acp['acl'][] = array( 1189 | 'type' => 'Group', 1190 | 'uri' => (string)$grantee->URI, 1191 | 'permission' => (string)$grant->Permission 1192 | ); 1193 | else continue; 1194 | } 1195 | } 1196 | } 1197 | return $acp; 1198 | } 1199 | 1200 | 1201 | /** 1202 | * Delete an object 1203 | * 1204 | * @param string $bucket Bucket name 1205 | * @param string $uri Object URI 1206 | * @return boolean 1207 | */ 1208 | public static function deleteObject($bucket, $uri) 1209 | { 1210 | $rest = new S3Request('DELETE', $bucket, $uri, self::$endpoint); 1211 | $rest = $rest->getResponse(); 1212 | if ($rest->error === false && $rest->code !== 204) 1213 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1214 | if ($rest->error !== false) 1215 | { 1216 | self::__triggerError(sprintf("S3::deleteObject(): [%s] %s", 1217 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1218 | return false; 1219 | } 1220 | return true; 1221 | } 1222 | 1223 | 1224 | /** 1225 | * Get a query string authenticated URL 1226 | * 1227 | * @param string $bucket Bucket name 1228 | * @param string $uri Object URI 1229 | * @param integer $lifetime Lifetime in seconds 1230 | * @param boolean $hostBucket Use the bucket name as the hostname 1231 | * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) 1232 | * @return string 1233 | */ 1234 | public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) 1235 | { 1236 | $expires = self::__getTime() + $lifetime; 1237 | $uri = str_replace(array('%2F', '%2B'), array('/', '+'), rawurlencode($uri)); 1238 | return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', 1239 | // $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires, 1240 | $hostBucket ? $bucket : self::$endpoint.'/'.$bucket, $uri, self::$__accessKey, $expires, 1241 | urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); 1242 | } 1243 | 1244 | 1245 | /** 1246 | * Get a CloudFront signed policy URL 1247 | * 1248 | * @param array $policy Policy 1249 | * @return string 1250 | */ 1251 | public static function getSignedPolicyURL($policy) 1252 | { 1253 | $data = json_encode($policy); 1254 | $signature = ''; 1255 | if (!openssl_sign($data, $signature, self::$__signingKeyResource)) return false; 1256 | 1257 | $encoded = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($data)); 1258 | $signature = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($signature)); 1259 | 1260 | $url = $policy['Statement'][0]['Resource'] . '?'; 1261 | foreach (array('Policy' => $encoded, 'Signature' => $signature, 'Key-Pair-Id' => self::$__signingKeyPairId) as $k => $v) 1262 | $url .= $k.'='.str_replace('%2F', '/', rawurlencode($v)).'&'; 1263 | return substr($url, 0, -1); 1264 | } 1265 | 1266 | 1267 | /** 1268 | * Get a CloudFront canned policy URL 1269 | * 1270 | * @param string $url URL to sign 1271 | * @param integer $lifetime URL lifetime 1272 | * @return string 1273 | */ 1274 | public static function getSignedCannedURL($url, $lifetime) 1275 | { 1276 | return self::getSignedPolicyURL(array( 1277 | 'Statement' => array( 1278 | array('Resource' => $url, 'Condition' => array( 1279 | 'DateLessThan' => array('AWS:EpochTime' => self::__getTime() + $lifetime) 1280 | )) 1281 | ) 1282 | )); 1283 | } 1284 | 1285 | 1286 | /** 1287 | * Get upload POST parameters for form uploads 1288 | * 1289 | * @param string $bucket Bucket name 1290 | * @param string $uriPrefix Object URI prefix 1291 | * @param constant $acl ACL constant 1292 | * @param integer $lifetime Lifetime in seconds 1293 | * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) 1294 | * @param string $successRedirect Redirect URL or 200 / 201 status code 1295 | * @param array $amzHeaders Array of x-amz-meta-* headers 1296 | * @param array $headers Array of request headers or content type as a string 1297 | * @param boolean $flashVars Includes additional "Filename" variable posted by Flash 1298 | * @return object 1299 | */ 1300 | public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, 1301 | $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) 1302 | { 1303 | // Create policy object 1304 | $policy = new stdClass; 1305 | $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (self::__getTime() + $lifetime)); 1306 | $policy->conditions = array(); 1307 | $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); 1308 | $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); 1309 | 1310 | $obj = new stdClass; // 200 for non-redirect uploads 1311 | if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) 1312 | $obj->success_action_status = (string)$successRedirect; 1313 | else // URL 1314 | $obj->success_action_redirect = $successRedirect; 1315 | array_push($policy->conditions, $obj); 1316 | 1317 | if ($acl !== self::ACL_PUBLIC_READ) 1318 | array_push($policy->conditions, array('eq', '$acl', $acl)); 1319 | 1320 | array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); 1321 | if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); 1322 | foreach (array_keys($headers) as $headerKey) 1323 | array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); 1324 | foreach ($amzHeaders as $headerKey => $headerVal) 1325 | { 1326 | $obj = new stdClass; 1327 | $obj->{$headerKey} = (string)$headerVal; 1328 | array_push($policy->conditions, $obj); 1329 | } 1330 | array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); 1331 | $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); 1332 | 1333 | // Create parameters 1334 | $params = new stdClass; 1335 | $params->AWSAccessKeyId = self::$__accessKey; 1336 | $params->key = $uriPrefix.'${filename}'; 1337 | $params->acl = $acl; 1338 | $params->policy = $policy; unset($policy); 1339 | $params->signature = self::__getHash($params->policy); 1340 | if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) 1341 | $params->success_action_status = (string)$successRedirect; 1342 | else 1343 | $params->success_action_redirect = $successRedirect; 1344 | foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; 1345 | foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; 1346 | return $params; 1347 | } 1348 | 1349 | 1350 | /** 1351 | * Create a CloudFront distribution 1352 | * 1353 | * @param string $bucket Bucket name 1354 | * @param boolean $enabled Enabled (true/false) 1355 | * @param array $cnames Array containing CNAME aliases 1356 | * @param string $comment Use the bucket name as the hostname 1357 | * @param string $defaultRootObject Default root object 1358 | * @param string $originAccessIdentity Origin access identity 1359 | * @param array $trustedSigners Array of trusted signers 1360 | * @return array | false 1361 | */ 1362 | public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = null, $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) 1363 | { 1364 | if (!extension_loaded('openssl')) 1365 | { 1366 | self::__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): %s", 1367 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1368 | return false; 1369 | } 1370 | $useSSL = self::$useSSL; 1371 | 1372 | self::$useSSL = true; // CloudFront requires SSL 1373 | $rest = new S3Request('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); 1374 | $rest->data = self::__getCloudFrontDistributionConfigXML( 1375 | $bucket.'.s3.amazonaws.com', 1376 | $enabled, 1377 | (string)$comment, 1378 | (string)microtime(true), 1379 | $cnames, 1380 | $defaultRootObject, 1381 | $originAccessIdentity, 1382 | $trustedSigners 1383 | ); 1384 | 1385 | $rest->size = strlen($rest->data); 1386 | $rest->setHeader('Content-Type', 'application/xml'); 1387 | $rest = self::__getCloudFrontResponse($rest); 1388 | 1389 | self::$useSSL = $useSSL; 1390 | 1391 | if ($rest->error === false && $rest->code !== 201) 1392 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1393 | if ($rest->error !== false) 1394 | { 1395 | self::__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): [%s] %s", 1396 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1397 | return false; 1398 | } elseif ($rest->body instanceof SimpleXMLElement) 1399 | return self::__parseCloudFrontDistributionConfig($rest->body); 1400 | return false; 1401 | } 1402 | 1403 | 1404 | /** 1405 | * Get CloudFront distribution info 1406 | * 1407 | * @param string $distributionId Distribution ID from listDistributions() 1408 | * @return array | false 1409 | */ 1410 | public static function getDistribution($distributionId) 1411 | { 1412 | if (!extension_loaded('openssl')) 1413 | { 1414 | self::__triggerError(sprintf("S3::getDistribution($distributionId): %s", 1415 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1416 | return false; 1417 | } 1418 | $useSSL = self::$useSSL; 1419 | 1420 | self::$useSSL = true; // CloudFront requires SSL 1421 | $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); 1422 | $rest = self::__getCloudFrontResponse($rest); 1423 | 1424 | self::$useSSL = $useSSL; 1425 | 1426 | if ($rest->error === false && $rest->code !== 200) 1427 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1428 | if ($rest->error !== false) 1429 | { 1430 | self::__triggerError(sprintf("S3::getDistribution($distributionId): [%s] %s", 1431 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1432 | return false; 1433 | } 1434 | elseif ($rest->body instanceof SimpleXMLElement) 1435 | { 1436 | $dist = self::__parseCloudFrontDistributionConfig($rest->body); 1437 | $dist['hash'] = $rest->headers['hash']; 1438 | $dist['id'] = $distributionId; 1439 | return $dist; 1440 | } 1441 | return false; 1442 | } 1443 | 1444 | 1445 | /** 1446 | * Update a CloudFront distribution 1447 | * 1448 | * @param array $dist Distribution array info identical to output of getDistribution() 1449 | * @return array | false 1450 | */ 1451 | public static function updateDistribution($dist) 1452 | { 1453 | if (!extension_loaded('openssl')) 1454 | { 1455 | self::__triggerError(sprintf("S3::updateDistribution({$dist['id']}): %s", 1456 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1457 | return false; 1458 | } 1459 | 1460 | $useSSL = self::$useSSL; 1461 | 1462 | self::$useSSL = true; // CloudFront requires SSL 1463 | $rest = new S3Request('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); 1464 | $rest->data = self::__getCloudFrontDistributionConfigXML( 1465 | $dist['origin'], 1466 | $dist['enabled'], 1467 | $dist['comment'], 1468 | $dist['callerReference'], 1469 | $dist['cnames'], 1470 | $dist['defaultRootObject'], 1471 | $dist['originAccessIdentity'], 1472 | $dist['trustedSigners'] 1473 | ); 1474 | 1475 | $rest->size = strlen($rest->data); 1476 | $rest->setHeader('If-Match', $dist['hash']); 1477 | $rest = self::__getCloudFrontResponse($rest); 1478 | 1479 | self::$useSSL = $useSSL; 1480 | 1481 | if ($rest->error === false && $rest->code !== 200) 1482 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1483 | if ($rest->error !== false) 1484 | { 1485 | self::__triggerError(sprintf("S3::updateDistribution({$dist['id']}): [%s] %s", 1486 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1487 | return false; 1488 | } else { 1489 | $dist = self::__parseCloudFrontDistributionConfig($rest->body); 1490 | $dist['hash'] = $rest->headers['hash']; 1491 | return $dist; 1492 | } 1493 | return false; 1494 | } 1495 | 1496 | 1497 | /** 1498 | * Delete a CloudFront distribution 1499 | * 1500 | * @param array $dist Distribution array info identical to output of getDistribution() 1501 | * @return boolean 1502 | */ 1503 | public static function deleteDistribution($dist) 1504 | { 1505 | if (!extension_loaded('openssl')) 1506 | { 1507 | self::__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): %s", 1508 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1509 | return false; 1510 | } 1511 | 1512 | $useSSL = self::$useSSL; 1513 | 1514 | self::$useSSL = true; // CloudFront requires SSL 1515 | $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); 1516 | $rest->setHeader('If-Match', $dist['hash']); 1517 | $rest = self::__getCloudFrontResponse($rest); 1518 | 1519 | self::$useSSL = $useSSL; 1520 | 1521 | if ($rest->error === false && $rest->code !== 204) 1522 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1523 | if ($rest->error !== false) 1524 | { 1525 | self::__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", 1526 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1527 | return false; 1528 | } 1529 | return true; 1530 | } 1531 | 1532 | 1533 | /** 1534 | * Get a list of CloudFront distributions 1535 | * 1536 | * @return array 1537 | */ 1538 | public static function listDistributions() 1539 | { 1540 | if (!extension_loaded('openssl')) 1541 | { 1542 | self::__triggerError(sprintf("S3::listDistributions(): [%s] %s", 1543 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1544 | return false; 1545 | } 1546 | 1547 | $useSSL = self::$useSSL; 1548 | self::$useSSL = true; // CloudFront requires SSL 1549 | $rest = new S3Request('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); 1550 | $rest = self::__getCloudFrontResponse($rest); 1551 | self::$useSSL = $useSSL; 1552 | 1553 | if ($rest->error === false && $rest->code !== 200) 1554 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1555 | if ($rest->error !== false) 1556 | { 1557 | self::__triggerError(sprintf("S3::listDistributions(): [%s] %s", 1558 | $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); 1559 | return false; 1560 | } 1561 | elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) 1562 | { 1563 | $list = array(); 1564 | if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) 1565 | { 1566 | //$info['marker'] = (string)$rest->body->Marker; 1567 | //$info['maxItems'] = (int)$rest->body->MaxItems; 1568 | //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; 1569 | } 1570 | foreach ($rest->body->DistributionSummary as $summary) 1571 | $list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary); 1572 | 1573 | return $list; 1574 | } 1575 | return array(); 1576 | } 1577 | 1578 | /** 1579 | * List CloudFront Origin Access Identities 1580 | * 1581 | * @return array 1582 | */ 1583 | public static function listOriginAccessIdentities() 1584 | { 1585 | if (!extension_loaded('openssl')) 1586 | { 1587 | self::__triggerError(sprintf("S3::listOriginAccessIdentities(): [%s] %s", 1588 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1589 | return false; 1590 | } 1591 | 1592 | self::$useSSL = true; // CloudFront requires SSL 1593 | $rest = new S3Request('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com'); 1594 | $rest = self::__getCloudFrontResponse($rest); 1595 | $useSSL = self::$useSSL; 1596 | 1597 | if ($rest->error === false && $rest->code !== 200) 1598 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1599 | if ($rest->error !== false) 1600 | { 1601 | trigger_error(sprintf("S3::listOriginAccessIdentities(): [%s] %s", 1602 | $rest->error['code'], $rest->error['message']), E_USER_WARNING); 1603 | return false; 1604 | } 1605 | 1606 | if (isset($rest->body->CloudFrontOriginAccessIdentitySummary)) 1607 | { 1608 | $identities = array(); 1609 | foreach ($rest->body->CloudFrontOriginAccessIdentitySummary as $identity) 1610 | if (isset($identity->S3CanonicalUserId)) 1611 | $identities[(string)$identity->Id] = array('id' => (string)$identity->Id, 's3CanonicalUserId' => (string)$identity->S3CanonicalUserId); 1612 | return $identities; 1613 | } 1614 | return false; 1615 | } 1616 | 1617 | 1618 | /** 1619 | * Invalidate objects in a CloudFront distribution 1620 | * 1621 | * Thanks to Martin Lindkvist for S3::invalidateDistribution() 1622 | * 1623 | * @param string $distributionId Distribution ID from listDistributions() 1624 | * @param array $paths Array of object paths to invalidate 1625 | * @return boolean 1626 | */ 1627 | public static function invalidateDistribution($distributionId, $paths) 1628 | { 1629 | if (!extension_loaded('openssl')) 1630 | { 1631 | self::__triggerError(sprintf("S3::invalidateDistribution(): [%s] %s", 1632 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1633 | return false; 1634 | } 1635 | 1636 | $useSSL = self::$useSSL; 1637 | self::$useSSL = true; // CloudFront requires SSL 1638 | $rest = new S3Request('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com'); 1639 | $rest->data = self::__getCloudFrontInvalidationBatchXML($paths, (string)microtime(true)); 1640 | $rest->size = strlen($rest->data); 1641 | $rest = self::__getCloudFrontResponse($rest); 1642 | self::$useSSL = $useSSL; 1643 | 1644 | if ($rest->error === false && $rest->code !== 201) 1645 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1646 | if ($rest->error !== false) 1647 | { 1648 | trigger_error(sprintf("S3::invalidate('{$distributionId}',{$paths}): [%s] %s", 1649 | $rest->error['code'], $rest->error['message']), E_USER_WARNING); 1650 | return false; 1651 | } 1652 | return true; 1653 | } 1654 | 1655 | 1656 | /** 1657 | * Get a InvalidationBatch DOMDocument 1658 | * 1659 | * @internal Used to create XML in invalidateDistribution() 1660 | * @param array $paths Paths to objects to invalidateDistribution 1661 | * @param int $callerReference 1662 | * @return string 1663 | */ 1664 | private static function __getCloudFrontInvalidationBatchXML($paths, $callerReference = '0') 1665 | { 1666 | $dom = new DOMDocument('1.0', 'UTF-8'); 1667 | $dom->formatOutput = true; 1668 | $invalidationBatch = $dom->createElement('InvalidationBatch'); 1669 | foreach ($paths as $path) 1670 | $invalidationBatch->appendChild($dom->createElement('Path', $path)); 1671 | 1672 | $invalidationBatch->appendChild($dom->createElement('CallerReference', $callerReference)); 1673 | $dom->appendChild($invalidationBatch); 1674 | return $dom->saveXML(); 1675 | } 1676 | 1677 | 1678 | /** 1679 | * List your invalidation batches for invalidateDistribution() in a CloudFront distribution 1680 | * 1681 | * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/ListInvalidation.html 1682 | * returned array looks like this: 1683 | * Array 1684 | * ( 1685 | * [I31TWB0CN9V6XD] => InProgress 1686 | * [IT3TFE31M0IHZ] => Completed 1687 | * [I12HK7MPO1UQDA] => Completed 1688 | * [I1IA7R6JKTC3L2] => Completed 1689 | * ) 1690 | * 1691 | * @param string $distributionId Distribution ID from listDistributions() 1692 | * @return array 1693 | */ 1694 | public static function getDistributionInvalidationList($distributionId) 1695 | { 1696 | if (!extension_loaded('openssl')) 1697 | { 1698 | self::__triggerError(sprintf("S3::getDistributionInvalidationList(): [%s] %s", 1699 | "CloudFront functionality requires SSL"), __FILE__, __LINE__); 1700 | return false; 1701 | } 1702 | 1703 | $useSSL = self::$useSSL; 1704 | self::$useSSL = true; // CloudFront requires SSL 1705 | $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com'); 1706 | $rest = self::__getCloudFrontResponse($rest); 1707 | self::$useSSL = $useSSL; 1708 | 1709 | if ($rest->error === false && $rest->code !== 200) 1710 | $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); 1711 | if ($rest->error !== false) 1712 | { 1713 | trigger_error(sprintf("S3::getDistributionInvalidationList('{$distributionId}'): [%s]", 1714 | $rest->error['code'], $rest->error['message']), E_USER_WARNING); 1715 | return false; 1716 | } 1717 | elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->InvalidationSummary)) 1718 | { 1719 | $list = array(); 1720 | foreach ($rest->body->InvalidationSummary as $summary) 1721 | $list[(string)$summary->Id] = (string)$summary->Status; 1722 | 1723 | return $list; 1724 | } 1725 | return array(); 1726 | } 1727 | 1728 | 1729 | /** 1730 | * Get a DistributionConfig DOMDocument 1731 | * 1732 | * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?PutConfig.html 1733 | * 1734 | * @internal Used to create XML in createDistribution() and updateDistribution() 1735 | * @param string $bucket S3 Origin bucket 1736 | * @param boolean $enabled Enabled (true/false) 1737 | * @param string $comment Comment to append 1738 | * @param string $callerReference Caller reference 1739 | * @param array $cnames Array of CNAME aliases 1740 | * @param string $defaultRootObject Default root object 1741 | * @param string $originAccessIdentity Origin access identity 1742 | * @param array $trustedSigners Array of trusted signers 1743 | * @return string 1744 | */ 1745 | private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array(), $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) 1746 | { 1747 | $dom = new DOMDocument('1.0', 'UTF-8'); 1748 | $dom->formatOutput = true; 1749 | $distributionConfig = $dom->createElement('DistributionConfig'); 1750 | $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2010-11-01/'); 1751 | 1752 | $origin = $dom->createElement('S3Origin'); 1753 | $origin->appendChild($dom->createElement('DNSName', $bucket)); 1754 | if ($originAccessIdentity !== null) $origin->appendChild($dom->createElement('OriginAccessIdentity', $originAccessIdentity)); 1755 | $distributionConfig->appendChild($origin); 1756 | 1757 | if ($defaultRootObject !== null) $distributionConfig->appendChild($dom->createElement('DefaultRootObject', $defaultRootObject)); 1758 | 1759 | $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); 1760 | foreach ($cnames as $cname) 1761 | $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); 1762 | if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); 1763 | $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); 1764 | 1765 | if (!empty($trustedSigners)) 1766 | { 1767 | $trusted = $dom->createElement('TrustedSigners'); 1768 | foreach ($trustedSigners as $id => $type) 1769 | $trusted->appendChild($id !== '' ? $dom->createElement($type, $id) : $dom->createElement($type)); 1770 | $distributionConfig->appendChild($trusted); 1771 | } 1772 | $dom->appendChild($distributionConfig); 1773 | //var_dump($dom->saveXML()); 1774 | return $dom->saveXML(); 1775 | } 1776 | 1777 | 1778 | /** 1779 | * Parse a CloudFront distribution config 1780 | * 1781 | * See http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?GetDistribution.html 1782 | * 1783 | * @internal Used to parse the CloudFront DistributionConfig node to an array 1784 | * @param object &$node DOMNode 1785 | * @return array 1786 | */ 1787 | private static function __parseCloudFrontDistributionConfig(&$node) 1788 | { 1789 | if (isset($node->DistributionConfig)) 1790 | return self::__parseCloudFrontDistributionConfig($node->DistributionConfig); 1791 | 1792 | $dist = array(); 1793 | if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) 1794 | { 1795 | $dist['id'] = (string)$node->Id; 1796 | $dist['status'] = (string)$node->Status; 1797 | $dist['time'] = strtotime((string)$node->LastModifiedTime); 1798 | $dist['domain'] = (string)$node->DomainName; 1799 | } 1800 | 1801 | if (isset($node->CallerReference)) 1802 | $dist['callerReference'] = (string)$node->CallerReference; 1803 | 1804 | if (isset($node->Enabled)) 1805 | $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; 1806 | 1807 | if (isset($node->S3Origin)) 1808 | { 1809 | if (isset($node->S3Origin->DNSName)) 1810 | $dist['origin'] = (string)$node->S3Origin->DNSName; 1811 | 1812 | $dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ? 1813 | (string)$node->S3Origin->OriginAccessIdentity : null; 1814 | } 1815 | 1816 | $dist['defaultRootObject'] = isset($node->DefaultRootObject) ? (string)$node->DefaultRootObject : null; 1817 | 1818 | $dist['cnames'] = array(); 1819 | if (isset($node->CNAME)) 1820 | foreach ($node->CNAME as $cname) 1821 | $dist['cnames'][(string)$cname] = (string)$cname; 1822 | 1823 | $dist['trustedSigners'] = array(); 1824 | if (isset($node->TrustedSigners)) 1825 | foreach ($node->TrustedSigners as $signer) 1826 | { 1827 | if (isset($signer->Self)) 1828 | $dist['trustedSigners'][''] = 'Self'; 1829 | elseif (isset($signer->KeyPairId)) 1830 | $dist['trustedSigners'][(string)$signer->KeyPairId] = 'KeyPairId'; 1831 | elseif (isset($signer->AwsAccountNumber)) 1832 | $dist['trustedSigners'][(string)$signer->AwsAccountNumber] = 'AwsAccountNumber'; 1833 | } 1834 | 1835 | $dist['comment'] = isset($node->Comment) ? (string)$node->Comment : null; 1836 | return $dist; 1837 | } 1838 | 1839 | 1840 | /** 1841 | * Grab CloudFront response 1842 | * 1843 | * @internal Used to parse the CloudFront S3Request::getResponse() output 1844 | * @param object &$rest S3Request instance 1845 | * @return object 1846 | */ 1847 | private static function __getCloudFrontResponse(&$rest) 1848 | { 1849 | $rest->getResponse(); 1850 | if ($rest->response->error === false && isset($rest->response->body) && 1851 | is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); 1854 | // Grab CloudFront errors 1855 | if (isset($rest->response->body->Error, $rest->response->body->Error->Code, 1856 | $rest->response->body->Error->Message)) 1857 | { 1858 | $rest->response->error = array( 1859 | 'code' => (string)$rest->response->body->Error->Code, 1860 | 'message' => (string)$rest->response->body->Error->Message 1861 | ); 1862 | unset($rest->response->body); 1863 | } 1864 | } 1865 | return $rest->response; 1866 | } 1867 | 1868 | 1869 | /** 1870 | * Get MIME type for file 1871 | * 1872 | * To override the putObject() Content-Type, add it to $requestHeaders 1873 | * 1874 | * To use fileinfo, ensure the MAGIC environment variable is set 1875 | * 1876 | * @internal Used to get mime types 1877 | * @param string &$file File path 1878 | * @return string 1879 | */ 1880 | private static function __getMIMEType(&$file) 1881 | { 1882 | static $exts = array( 1883 | 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif', 1884 | 'png' => 'image/png', 'ico' => 'image/x-icon', 'pdf' => 'application/pdf', 1885 | 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'svg' => 'image/svg+xml', 1886 | 'svgz' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 1887 | 'zip' => 'application/zip', 'gz' => 'application/x-gzip', 1888 | 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', 1889 | 'bz2' => 'application/x-bzip2', 'rar' => 'application/x-rar-compressed', 1890 | 'exe' => 'application/x-msdownload', 'msi' => 'application/x-msdownload', 1891 | 'cab' => 'application/vnd.ms-cab-compressed', 'txt' => 'text/plain', 1892 | 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', 1893 | 'css' => 'text/css', 'js' => 'text/javascript', 1894 | 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', 1895 | 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', 1896 | 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 1897 | 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' 1898 | ); 1899 | 1900 | $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); 1901 | if (isset($exts[$ext])) return $exts[$ext]; 1902 | 1903 | // Use fileinfo if available 1904 | if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && 1905 | ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) 1906 | { 1907 | if (($type = finfo_file($finfo, $file)) !== false) 1908 | { 1909 | // Remove the charset and grab the last content-type 1910 | $type = explode(' ', str_replace('; charset=', ';charset=', $type)); 1911 | $type = array_pop($type); 1912 | $type = explode(';', $type); 1913 | $type = trim(array_shift($type)); 1914 | } 1915 | finfo_close($finfo); 1916 | if ($type !== false && strlen($type) > 0) return $type; 1917 | } 1918 | 1919 | return 'application/octet-stream'; 1920 | } 1921 | 1922 | 1923 | /** 1924 | * Get the current time 1925 | * 1926 | * @internal Used to apply offsets to sytem time 1927 | * @return integer 1928 | */ 1929 | public static function __getTime() 1930 | { 1931 | return time() + self::$__timeOffset; 1932 | } 1933 | 1934 | 1935 | /** 1936 | * Generate the auth string: "AWS AccessKey:Signature" 1937 | * 1938 | * @internal Used by S3Request::getResponse() 1939 | * @param string $string String to sign 1940 | * @return string 1941 | */ 1942 | public static function __getSignature($string) 1943 | { 1944 | return 'AWS '.self::$__accessKey.':'.self::__getHash($string); 1945 | } 1946 | 1947 | 1948 | /** 1949 | * Creates a HMAC-SHA1 hash 1950 | * 1951 | * This uses the hash extension if loaded 1952 | * 1953 | * @internal Used by __getSignature() 1954 | * @param string $string String to sign 1955 | * @return string 1956 | */ 1957 | private static function __getHash($string) 1958 | { 1959 | return base64_encode(extension_loaded('hash') ? 1960 | hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1( 1961 | (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . 1962 | pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^ 1963 | (str_repeat(chr(0x36), 64))) . $string))))); 1964 | } 1965 | 1966 | 1967 | /** 1968 | * Generate the headers for AWS Signature V4 1969 | * @internal Used by S3Request::getResponse() 1970 | * @param array $aheaders amzHeaders 1971 | * @param array $headers 1972 | * @param string $method 1973 | * @param string $uri 1974 | * @param string $data 1975 | * @return array $headers 1976 | */ 1977 | public static function __getSignatureV4($aHeaders, $headers, $method='GET', $uri='', $data = '') 1978 | { 1979 | $service = 's3'; 1980 | $region = S3::getRegion(); 1981 | 1982 | $algorithm = 'AWS4-HMAC-SHA256'; 1983 | $amzHeaders = array(); 1984 | $amzRequests = array(); 1985 | 1986 | $amzDate = gmdate( 'Ymd\THis\Z' ); 1987 | $amzDateStamp = gmdate( 'Ymd' ); 1988 | 1989 | // amz-date ISO8601 format ? for aws request 1990 | $amzHeaders['x-amz-date'] = $amzDate; 1991 | 1992 | // CanonicalHeaders 1993 | foreach ( $headers as $k => $v ) { 1994 | $amzHeaders[ strtolower( $k ) ] = trim( $v ); 1995 | } 1996 | foreach ( $aHeaders as $k => $v ) { 1997 | $amzHeaders[ strtolower( $k ) ] = trim( $v ); 1998 | } 1999 | uksort( $amzHeaders, 'strcmp' ); 2000 | 2001 | // payload 2002 | $payloadHash = isset($amzHeaders['x-amz-content-sha256']) ? $amzHeaders['x-amz-content-sha256'] : hash('sha256', $data); 2003 | 2004 | // parameters 2005 | $parameters = array(); 2006 | if (strpos($uri, '?')) { 2007 | list ($uri, $query_str) = @explode("?", $uri); 2008 | parse_str($query_str, $parameters); 2009 | } 2010 | 2011 | // CanonicalRequests 2012 | $amzRequests[] = $method; 2013 | $uriQmPos = strpos($uri, '?'); 2014 | $amzRequests[] = ($uriQmPos === false ? $uri : substr($uri, 0, $uriQmPos)); 2015 | $amzRequests[] = http_build_query($parameters); 2016 | // add header as string to requests 2017 | foreach ( $amzHeaders as $k => $v ) { 2018 | $amzRequests[] = $k . ':' . $v; 2019 | } 2020 | // add a blank entry so we end up with an extra line break 2021 | $amzRequests[] = ''; 2022 | // SignedHeaders 2023 | $amzRequests[] = implode( ';', array_keys( $amzHeaders ) ); 2024 | // payload hash 2025 | $amzRequests[] = $payloadHash; 2026 | // request as string 2027 | $amzRequestStr = implode("\n", $amzRequests); 2028 | 2029 | // CredentialScope 2030 | $credentialScope = array(); 2031 | $credentialScope[] = $amzDateStamp; 2032 | $credentialScope[] = $region; 2033 | $credentialScope[] = $service; 2034 | $credentialScope[] = 'aws4_request'; 2035 | 2036 | // stringToSign 2037 | $stringToSign = array(); 2038 | $stringToSign[] = $algorithm; 2039 | $stringToSign[] = $amzDate; 2040 | $stringToSign[] = implode('/', $credentialScope); 2041 | $stringToSign[] = hash('sha256', $amzRequestStr); 2042 | // as string 2043 | $stringToSignStr = implode("\n", $stringToSign); 2044 | 2045 | // Make Signature 2046 | $kSecret = 'AWS4' . self::$__secretKey; 2047 | $kDate = hash_hmac( 'sha256', $amzDateStamp, $kSecret, true ); 2048 | $kRegion = hash_hmac( 'sha256', $region, $kDate, true ); 2049 | $kService = hash_hmac( 'sha256', $service, $kRegion, true ); 2050 | $kSigning = hash_hmac( 'sha256', 'aws4_request', $kService, true ); 2051 | 2052 | $signature = hash_hmac( 'sha256', $stringToSignStr, $kSigning ); 2053 | 2054 | $authorization = array( 2055 | 'Credential=' . self::$__accessKey . '/' . implode( '/', $credentialScope ), 2056 | 'SignedHeaders=' . implode( ';', array_keys( $amzHeaders ) ), 2057 | 'Signature=' . $signature, 2058 | ); 2059 | 2060 | $authorizationStr = $algorithm . ' ' . implode( ',', $authorization ); 2061 | 2062 | $resultHeaders = array( 2063 | 'X-AMZ-DATE' => $amzDate, 2064 | 'Authorization' => $authorizationStr 2065 | ); 2066 | if (!isset($aHeaders['x-amz-content-sha256'])) $resultHeaders['x-amz-content-sha256'] = $payloadHash; 2067 | 2068 | return $resultHeaders; 2069 | } 2070 | 2071 | 2072 | } 2073 | 2074 | /** 2075 | * S3 Request class 2076 | * 2077 | * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class 2078 | * @version 0.5.0-dev 2079 | */ 2080 | final class S3Request 2081 | { 2082 | /** 2083 | * AWS URI 2084 | * 2085 | * @var string 2086 | * @access private 2087 | */ 2088 | private $endpoint; 2089 | 2090 | /** 2091 | * Verb 2092 | * 2093 | * @var string 2094 | * @access private 2095 | */ 2096 | private $verb; 2097 | 2098 | /** 2099 | * S3 bucket name 2100 | * 2101 | * @var string 2102 | * @access private 2103 | */ 2104 | private $bucket; 2105 | 2106 | /** 2107 | * Object URI 2108 | * 2109 | * @var string 2110 | * @access private 2111 | */ 2112 | private $uri; 2113 | 2114 | /** 2115 | * Final object URI 2116 | * 2117 | * @var string 2118 | * @access private 2119 | */ 2120 | private $resource = ''; 2121 | 2122 | /** 2123 | * Additional request parameters 2124 | * 2125 | * @var array 2126 | * @access private 2127 | */ 2128 | private $parameters = array(); 2129 | 2130 | /** 2131 | * Amazon specific request headers 2132 | * 2133 | * @var array 2134 | * @access private 2135 | */ 2136 | private $amzHeaders = array(); 2137 | 2138 | /** 2139 | * HTTP request headers 2140 | * 2141 | * @var array 2142 | * @access private 2143 | */ 2144 | private $headers = array( 2145 | 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' 2146 | ); 2147 | 2148 | /** 2149 | * Use HTTP PUT? 2150 | * 2151 | * @var bool 2152 | * @access public 2153 | */ 2154 | public $fp = false; 2155 | 2156 | /** 2157 | * PUT file size 2158 | * 2159 | * @var int 2160 | * @access public 2161 | */ 2162 | public $size = 0; 2163 | 2164 | /** 2165 | * PUT post fields 2166 | * 2167 | * @var array 2168 | * @access public 2169 | */ 2170 | public $data = false; 2171 | 2172 | /** 2173 | * S3 request respone 2174 | * 2175 | * @var object 2176 | * @access public 2177 | */ 2178 | public $response; 2179 | 2180 | 2181 | /** 2182 | * Constructor 2183 | * 2184 | * @param string $verb Verb 2185 | * @param string $bucket Bucket name 2186 | * @param string $uri Object URI 2187 | * @param string $endpoint AWS endpoint URI 2188 | * @return mixed 2189 | */ 2190 | function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com') 2191 | { 2192 | 2193 | $this->endpoint = $endpoint; 2194 | $this->verb = $verb; 2195 | $this->bucket = $bucket; 2196 | $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; 2197 | 2198 | //if ($this->bucket !== '') 2199 | // $this->resource = '/'.$this->bucket.$this->uri; 2200 | //else 2201 | // $this->resource = $this->uri; 2202 | 2203 | if ($this->bucket !== '') 2204 | { 2205 | if ($this->__dnsBucketName($this->bucket)) 2206 | { 2207 | $this->headers['Host'] = $this->bucket.'.'.$this->endpoint; 2208 | $this->resource = '/'.$this->bucket.$this->uri; 2209 | } 2210 | else 2211 | { 2212 | $this->headers['Host'] = $this->endpoint; 2213 | $this->uri = $this->uri; 2214 | if ($this->bucket !== '') $this->uri = '/'.$this->bucket.$this->uri; 2215 | $this->bucket = ''; 2216 | $this->resource = $this->uri; 2217 | } 2218 | } 2219 | else 2220 | { 2221 | $this->headers['Host'] = $this->endpoint; 2222 | $this->resource = $this->uri; 2223 | } 2224 | 2225 | 2226 | $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); 2227 | $this->response = new \STDClass; 2228 | $this->response->error = false; 2229 | $this->response->body = null; 2230 | $this->response->headers = array(); 2231 | } 2232 | 2233 | 2234 | /** 2235 | * Set request parameter 2236 | * 2237 | * @param string $key Key 2238 | * @param string $value Value 2239 | * @return void 2240 | */ 2241 | public function setParameter($key, $value) 2242 | { 2243 | $this->parameters[$key] = $value; 2244 | } 2245 | 2246 | 2247 | /** 2248 | * Set request header 2249 | * 2250 | * @param string $key Key 2251 | * @param string $value Value 2252 | * @return void 2253 | */ 2254 | public function setHeader($key, $value) 2255 | { 2256 | $this->headers[$key] = $value; 2257 | } 2258 | 2259 | 2260 | /** 2261 | * Set x-amz-meta-* header 2262 | * 2263 | * @param string $key Key 2264 | * @param string $value Value 2265 | * @return void 2266 | */ 2267 | public function setAmzHeader($key, $value) 2268 | { 2269 | $this->amzHeaders[$key] = $value; 2270 | } 2271 | 2272 | 2273 | /** 2274 | * Get the S3 response 2275 | * 2276 | * @return object | false 2277 | */ 2278 | public function getResponse() 2279 | { 2280 | $query = ''; 2281 | if (sizeof($this->parameters) > 0) 2282 | { 2283 | $query = substr($this->uri, -1) !== '?' ? '?' : '&'; 2284 | foreach ($this->parameters as $var => $value) 2285 | if ($value == null || $value == '') $query .= $var.'&'; 2286 | else $query .= $var.'='.rawurlencode($value).'&'; 2287 | $query = substr($query, 0, -1); 2288 | $this->uri .= $query; 2289 | 2290 | if (array_key_exists('acl', $this->parameters) || 2291 | array_key_exists('location', $this->parameters) || 2292 | array_key_exists('torrent', $this->parameters) || 2293 | array_key_exists('website', $this->parameters) || 2294 | array_key_exists('logging', $this->parameters)) 2295 | $this->resource .= $query; 2296 | } 2297 | $url = (S3::$useSSL ? 'https://' : 'http://') . ($this->headers['Host'] !== '' ? $this->headers['Host'] : $this->endpoint) . $this->uri; 2298 | 2299 | //var_dump('bucket: ' . $this->bucket, 'uri: ' . $this->uri, 'resource: ' . $this->resource, 'url: ' . $url); 2300 | 2301 | // Basic setup 2302 | $curl = curl_init(); 2303 | curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); 2304 | 2305 | if (S3::$useSSL) 2306 | { 2307 | // Set protocol version 2308 | curl_setopt($curl, CURLOPT_SSLVERSION, S3::$useSSLVersion); 2309 | 2310 | // SSL Validation can now be optional for those with broken OpenSSL installations 2311 | curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, S3::$useSSLValidation ? 2 : 0); 2312 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, S3::$useSSLValidation ? 1 : 0); 2313 | 2314 | if (S3::$sslKey !== null) curl_setopt($curl, CURLOPT_SSLKEY, S3::$sslKey); 2315 | if (S3::$sslCert !== null) curl_setopt($curl, CURLOPT_SSLCERT, S3::$sslCert); 2316 | if (S3::$sslCACert !== null) curl_setopt($curl, CURLOPT_CAINFO, S3::$sslCACert); 2317 | } 2318 | 2319 | curl_setopt($curl, CURLOPT_URL, $url); 2320 | 2321 | if (S3::$proxy != null && isset(S3::$proxy['host'])) 2322 | { 2323 | curl_setopt($curl, CURLOPT_PROXY, S3::$proxy['host']); 2324 | curl_setopt($curl, CURLOPT_PROXYTYPE, S3::$proxy['type']); 2325 | if (isset(S3::$proxy['user'], S3::$proxy['pass']) && S3::$proxy['user'] != null && S3::$proxy['pass'] != null) 2326 | curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', S3::$proxy['user'], S3::$proxy['pass'])); 2327 | } 2328 | 2329 | // Headers 2330 | $headers = array(); $amz = array(); 2331 | foreach ($this->amzHeaders as $header => $value) 2332 | if (strlen($value) > 0) $headers[] = $header.': '.$value; 2333 | foreach ($this->headers as $header => $value) 2334 | if (strlen($value) > 0) $headers[] = $header.': '.$value; 2335 | 2336 | // Collect AMZ headers for signature 2337 | foreach ($this->amzHeaders as $header => $value) 2338 | if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; 2339 | 2340 | // AMZ headers must be sorted 2341 | if (sizeof($amz) > 0) 2342 | { 2343 | //sort($amz); 2344 | usort($amz, array(&$this, '__sortMetaHeadersCmp')); 2345 | $amz = "\n".implode("\n", $amz); 2346 | } else $amz = ''; 2347 | 2348 | if (S3::hasAuth()) 2349 | { 2350 | // Authorization string (CloudFront stringToSign should only contain a date) 2351 | if ($this->headers['Host'] == 'cloudfront.amazonaws.com') 2352 | $headers[] = 'Authorization: ' . S3::__getSignature($this->headers['Date']); 2353 | else 2354 | { 2355 | if (S3::$signVer == 'v2') 2356 | { 2357 | $headers[] = 'Authorization: ' . S3::__getSignature( 2358 | $this->verb."\n". 2359 | $this->headers['Content-MD5']."\n". 2360 | $this->headers['Content-Type']."\n". 2361 | $this->headers['Date'].$amz."\n". 2362 | $this->resource 2363 | ); 2364 | } else { 2365 | $amzHeaders = S3::__getSignatureV4( 2366 | $this->amzHeaders, 2367 | $this->headers, 2368 | $this->verb, 2369 | $this->uri, 2370 | $this->data 2371 | ); 2372 | foreach ($amzHeaders as $k => $v) { 2373 | $headers[] = $k .': '. $v; 2374 | } 2375 | } 2376 | } 2377 | } 2378 | 2379 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 2380 | curl_setopt($curl, CURLOPT_HEADER, false); 2381 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); 2382 | curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); 2383 | curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); 2384 | curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 2385 | 2386 | // Request types 2387 | switch ($this->verb) 2388 | { 2389 | case 'GET': break; 2390 | case 'PUT': case 'POST': // POST only used for CloudFront 2391 | if ($this->fp !== false) 2392 | { 2393 | curl_setopt($curl, CURLOPT_PUT, true); 2394 | curl_setopt($curl, CURLOPT_INFILE, $this->fp); 2395 | if ($this->size >= 0) 2396 | curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); 2397 | } 2398 | elseif ($this->data !== false) 2399 | { 2400 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); 2401 | curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); 2402 | } 2403 | else 2404 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); 2405 | break; 2406 | case 'HEAD': 2407 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); 2408 | curl_setopt($curl, CURLOPT_NOBODY, true); 2409 | break; 2410 | case 'DELETE': 2411 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); 2412 | break; 2413 | default: break; 2414 | } 2415 | 2416 | // set curl progress function callback 2417 | if (S3::$progressFunction) { 2418 | curl_setopt($curl, CURLOPT_NOPROGRESS, false); 2419 | curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, S3::$progressFunction); 2420 | } 2421 | 2422 | // Execute, grab errors 2423 | if (curl_exec($curl)) 2424 | $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 2425 | else 2426 | $this->response->error = array( 2427 | 'code' => curl_errno($curl), 2428 | 'message' => curl_error($curl), 2429 | 'resource' => $this->resource 2430 | ); 2431 | 2432 | @curl_close($curl); 2433 | 2434 | // Parse body into XML 2435 | if ($this->response->error === false && isset($this->response->headers['type']) && 2436 | $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) 2437 | { 2438 | $this->response->body = simplexml_load_string($this->response->body); 2439 | 2440 | // Grab S3 errors 2441 | if (!in_array($this->response->code, array(200, 204, 206)) && 2442 | isset($this->response->body->Code, $this->response->body->Message)) 2443 | { 2444 | $this->response->error = array( 2445 | 'code' => (string)$this->response->body->Code, 2446 | 'message' => (string)$this->response->body->Message 2447 | ); 2448 | if (isset($this->response->body->Resource)) 2449 | $this->response->error['resource'] = (string)$this->response->body->Resource; 2450 | unset($this->response->body); 2451 | } 2452 | } 2453 | 2454 | // Clean up file resources 2455 | if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); 2456 | 2457 | return $this->response; 2458 | } 2459 | 2460 | /** 2461 | * Sort compare for meta headers 2462 | * 2463 | * @internal Used to sort x-amz meta headers 2464 | * @param string $a String A 2465 | * @param string $b String B 2466 | * @return integer 2467 | */ 2468 | private function __sortMetaHeadersCmp($a, $b) 2469 | { 2470 | $lenA = strpos($a, ':'); 2471 | $lenB = strpos($b, ':'); 2472 | $minLen = min($lenA, $lenB); 2473 | $ncmp = strncmp($a, $b, $minLen); 2474 | if ($lenA == $lenB) return $ncmp; 2475 | if (0 == $ncmp) return $lenA < $lenB ? -1 : 1; 2476 | return $ncmp; 2477 | } 2478 | 2479 | /** 2480 | * CURL write callback 2481 | * 2482 | * @param resource &$curl CURL resource 2483 | * @param string &$data Data 2484 | * @return integer 2485 | */ 2486 | private function __responseWriteCallback(&$curl, &$data) 2487 | { 2488 | if (in_array($this->response->code, array(200, 206)) && $this->fp !== false) 2489 | return fwrite($this->fp, $data); 2490 | else 2491 | $this->response->body .= $data; 2492 | return strlen($data); 2493 | } 2494 | 2495 | 2496 | /** 2497 | * Check DNS conformity 2498 | * 2499 | * @param string $bucket Bucket name 2500 | * @return boolean 2501 | */ 2502 | private function __dnsBucketName($bucket) 2503 | { 2504 | if (strlen($bucket) > 63 || preg_match("/[^a-z0-9\.-]/", $bucket) > 0) return false; 2505 | if (S3::$useSSL && strstr($bucket, '.') !== false) return false; 2506 | if (strstr($bucket, '-.') !== false) return false; 2507 | if (strstr($bucket, '..') !== false) return false; 2508 | if (!preg_match("/^[0-9a-z]/", $bucket)) return false; 2509 | if (!preg_match("/[0-9a-z]$/", $bucket)) return false; 2510 | return true; 2511 | } 2512 | 2513 | 2514 | /** 2515 | * CURL header callback 2516 | * 2517 | * @param resource $curl CURL resource 2518 | * @param string $data Data 2519 | * @return integer 2520 | */ 2521 | private function __responseHeaderCallback($curl, $data) 2522 | { 2523 | if (($strlen = strlen($data)) <= 2) return $strlen; 2524 | if (substr($data, 0, 4) == 'HTTP') 2525 | $this->response->code = (int)substr($data, 9, 3); 2526 | else 2527 | { 2528 | $data = trim($data); 2529 | if (strpos($data, ': ') === false) return $strlen; 2530 | list($header, $value) = explode(': ', $data, 2); 2531 | if ($header == 'Last-Modified') 2532 | $this->response->headers['time'] = strtotime($value); 2533 | elseif ($header == 'Date') 2534 | $this->response->headers['date'] = strtotime($value); 2535 | elseif ($header == 'Content-Length') 2536 | $this->response->headers['size'] = (int)$value; 2537 | elseif ($header == 'Content-Type') 2538 | $this->response->headers['type'] = $value; 2539 | elseif ($header == 'ETag') 2540 | $this->response->headers['hash'] = $value[0] == '"' ? substr($value, 1, -1) : $value; 2541 | elseif (preg_match('/^x-amz-meta-.*$/', $header)) 2542 | $this->response->headers[$header] = $value; 2543 | } 2544 | return $strlen; 2545 | } 2546 | 2547 | } 2548 | 2549 | /** 2550 | * S3 exception class 2551 | * 2552 | * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class 2553 | * @version 0.5.0-dev 2554 | */ 2555 | 2556 | class S3Exception extends \Exception { 2557 | /** 2558 | * Class constructor 2559 | * 2560 | * @param string $message Exception message 2561 | * @param string $file File in which exception was created 2562 | * @param string $line Line number on which exception was created 2563 | * @param int $code Exception code 2564 | */ 2565 | function __construct($message, $file, $line, $code = 0) 2566 | { 2567 | parent::__construct($message, $code); 2568 | $this->file = $file; 2569 | $this->line = $line; 2570 | } 2571 | } --------------------------------------------------------------------------------