├── .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 | [](https://github.com/helloakn/zframework) [](https://github.com/helloakn/zframework) [](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 | }
--------------------------------------------------------------------------------