├── EGithub.php ├── ESimpleGithub.php ├── README.md └── models ├── EGithubGist.php ├── EGithubGistComment.php ├── EGithubGistFile.php ├── EGithubGistHistory.php ├── EGithubGistHistoryChangeStatus.php ├── EGithubModel.php ├── EGithubUser.php └── EGithubUserPlan.php /EGithub.php: -------------------------------------------------------------------------------- 1 | authMode == 0) { 46 | // //@todo: add auth params 47 | // } 48 | 49 | // @todo: implement POST/DELETE/... request types 50 | curl_setopt($c, CURLOPT_URL, $url); 51 | curl_setopt($c, CURLOPT_RETURNTRANSFER, true); 52 | 53 | curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0); 54 | curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0); 55 | 56 | $response = curl_exec($c); 57 | curl_close($c); 58 | 59 | return array(200, json_decode($response)); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /ESimpleGithub.php: -------------------------------------------------------------------------------- 1 | _rateLimit; 13 | } 14 | 15 | public function getRateLimitRemaining() 16 | { 17 | return $this->_rateLimitRemaining; 18 | } 19 | 20 | /** 21 | * @param string $url 22 | */ 23 | public function setApiBaseUrl($url) 24 | { 25 | $this->_apiBaseUrl = trim($url, '/'); 26 | } 27 | 28 | /** 29 | * @return string 30 | */ 31 | public function getApiBaseUrl() 32 | { 33 | return $this->_apiBaseUrl; 34 | } 35 | 36 | /** 37 | * do a github request and return array 38 | * 39 | * @param string $url 40 | * @param string $requestType GET POST PUT DELETE etc... 41 | * @param array $data 42 | * @return array 43 | */ 44 | protected function requestInternal($url, $requestType='GET', $data=array(), $header=true) 45 | { 46 | $c = curl_init(); 47 | 48 | curl_setopt($c, CURLOPT_URL, $url); 49 | curl_setopt($c, CURLOPT_RETURNTRANSFER, true); 50 | 51 | curl_setopt($c, CURLOPT_USERAGENT, "yiiext.components.github-api (v 0.5dev)"); 52 | curl_setopt($c, CURLOPT_TIMEOUT, 30); 53 | 54 | curl_setopt($c, CURLOPT_HEADER, $header); // returns header in output 55 | curl_setopt($c, CURLOPT_FOLLOWLOCATION, true); 56 | 57 | //curl_setopt($c, CURLOPT_HTTPHEADER, array()); 58 | 59 | switch($requestType) 60 | { 61 | default: 62 | case 'GET': 63 | curl_setopt($c, CURLOPT_HTTPGET, true); 64 | break; 65 | case 'POST': 66 | curl_setopt($c, CURLOPT_POST, true); 67 | curl_setopt($c, CURLOPT_POSTFIELDS, $data); 68 | break; 69 | case 'PUT': 70 | curl_setopt($c, CURLOPT_PUT, true); 71 | // curl_setopt($c, CURLOPT_INFILE, $file=fopen(...)); 72 | // curl_setopt($c, CURLOPT_INFILESIZE, strlen($data)); 73 | break; 74 | } 75 | 76 | curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0); 77 | curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0); 78 | 79 | $response = curl_exec($c); 80 | curl_close($c); 81 | 82 | return $response; 83 | } 84 | 85 | /** 86 | * do a github request and return array 87 | * 88 | * @param string $url 89 | * @param string $requestType GET POST PUT DELETE etc... 90 | * @param array $data 91 | * @return array 92 | */ 93 | public function request($url, $requestType='GET', $data=array()) 94 | { 95 | $url = $this->getApiBaseUrl() . $url; 96 | 97 | $response = $this->requestInternal($url, $requestType, $data); 98 | 99 | // parse response 100 | $header = true; 101 | $content = array(); 102 | $status = 200; 103 | foreach(explode("\r\n", $response) as $line) 104 | { 105 | if ($line == '') { 106 | $header = false; 107 | } 108 | else if ($header) { 109 | $line = explode(': ', $line); 110 | switch($line[0]) { 111 | // case 'Content-Type': // application/json; charset=utf-8 112 | // break; 113 | case 'Status': $status = substr($line[1], 0, 3); break; 114 | case 'X-RateLimit-Limit': $this->_rateLimit = intval($line[1]); break; 115 | case 'X-RateLimit-Remaining': $this->_rateLimitRemaining = intval($line[1]); break; 116 | } 117 | } else { 118 | $content[] = $line; 119 | } 120 | } 121 | 122 | return array($status, json_decode(implode("\n", $content))); 123 | } 124 | 125 | public function getFile($user, $repo, $branch, $file) 126 | { 127 | $url = 'https://raw.github.com/' . $user . '/' . $repo . '/' . $branch . '/' . ltrim($file, '/'); 128 | 129 | return $this->requestInternal($url, 'GET', array(), false); 130 | } 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | this extension is not ready to use yet. 2 | Class API will change in the future and it is not complete yet. 3 | -------------------------------------------------------------------------------- /models/EGithubGist.php: -------------------------------------------------------------------------------- 1 | array(static::RELATION_ONE, 'EGithubUser'), 36 | 'files' => array(static::RELATION_MANY, 'EGithubGistFile'), 37 | 'forks' => array(static::RELATION_MANY, 'EGithubGist'), 38 | 'history' => array(static::RELATION_MANY, 'EGithubGistHistory'), 39 | ); 40 | } 41 | 42 | protected function findScopes() 43 | { 44 | return array( 45 | 'find' => '/gists', 46 | 'default' => '/gists', 47 | 'public' => '/gists/public', 48 | 'starred' => '/gists/starred', 49 | ); 50 | } 51 | 52 | 53 | public function create() 54 | { 55 | $this->apiRequest('/gists', 'POST', $this->getAttributes()); 56 | } 57 | 58 | protected function postUrls() 59 | { 60 | return array( 61 | 'patch' => array( 62 | 'url' => '/gists/:id', 63 | 'requestType' => 'PATCH', 64 | ), 65 | 'star' => array( 66 | 'url' => '/gists/:id/star', 67 | 'requestType' => 'PUT', 68 | ), 69 | 'unstar' => array( 70 | 'url' => '/gists/:id/star', 71 | 'requestType' => 'DELETE', 72 | ), 73 | 'isStarred' => array( 74 | 'url' => '/gists/:id/star', 75 | 'requestType' => 'GET', 76 | ), 77 | ); 78 | $findUrl = '/gists/:id'; 79 | } 80 | 81 | public static function model($className=__CLASS__) 82 | { 83 | return parent::model($className); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /models/EGithubGistComment.php: -------------------------------------------------------------------------------- 1 | array(static::RELATION_ONE, 'EGithubUser'), 28 | 'change_status' => array(static::RELATION_ONE, 'EGithubGistHistoryChangeStatus'), 29 | ); 30 | } 31 | 32 | public static function model($className=__CLASS__) 33 | { 34 | return parent::model($className); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /models/EGithubGistHistoryChangeStatus.php: -------------------------------------------------------------------------------- 1 | attribute value 10 | private $_related = array(); 11 | 12 | // @todo: add relation 13 | 14 | // @todo: mark new records 15 | 16 | // @todo: skope 17 | 18 | 19 | private static $_models=array(); // class name => model 20 | 21 | /** 22 | * Returns the static model of the specified EGithubModel class. 23 | * The model returned is a static instance of the class. 24 | * It is provided for invoking class-level methods (something similar to static class methods.) 25 | * 26 | * EVERY derived class must override this method as follows, 27 | *
28 | * public static function model($className=__CLASS__) 29 | * { 30 | * return parent::model($className); 31 | * } 32 | *33 | * 34 | * @param string $className EGithubModel class name. 35 | * @return EGithubModel model class instance. 36 | */ 37 | public static function model($className=__CLASS__) 38 | { 39 | if(isset(self::$_models[$className])) 40 | return self::$_models[$className]; 41 | else 42 | { 43 | $model=self::$_models[$className]=new $className(null); 44 | //$model->attachBehaviors($model->behaviors()); 45 | return $model; 46 | } 47 | } 48 | 49 | //abstract public function attributeNames(); 50 | 51 | const RELATION_ONE = 0; 52 | const RELATION_MANY = 1; 53 | public function relations() 54 | { 55 | return array(); 56 | } 57 | 58 | protected function findScopes() 59 | { 60 | throw new CException('This model does not provide find methods. It can only be created over relations.'); 61 | } 62 | 63 | public function findAll($scope='default') 64 | { 65 | $scopes = $this->findScopes(); 66 | if (isset($scopes[$scope])) { 67 | $url = $scopes[$scope]; 68 | } else { 69 | throw new CException('Provided scope ' . $scope . ' is not defined by findScopes().'); 70 | } 71 | list($responseCode, $data) = EGithub::githubRequest($url, 'GET', array()); 72 | if ($responseCode != 200) { 73 | throw new EGithubModelRequestException($responseCode, 'Find request failed.'); 74 | } 75 | $class = get_class($this); 76 | $models = array(); 77 | foreach($data as $modelData) { 78 | $model = new $class(); 79 | $model->populate($modelData); 80 | $model->_fullPopulated = true; 81 | $models[] = $model; 82 | } 83 | return $models; 84 | } 85 | 86 | public function find($id) 87 | { 88 | $scopes = $this->findScopes(); 89 | if (isset($scopes['find'])) { 90 | $url = $scopes['find'] . '/' . urlencode($id); 91 | } else { 92 | throw new CException('Default scope for find is not defined by findScopes().'); 93 | } 94 | list($responseCode, $data) = EGithub::githubRequest($url, 'GET', array()); 95 | if ($responseCode != 200) { 96 | throw new EGithubModelRequestException($responseCode, 'Find request failed.'); 97 | } 98 | $class = get_class($this); 99 | $model = new $class(); 100 | $model->populate($data); 101 | $model->_fullPopulated = true; 102 | return $model; 103 | } 104 | 105 | public function apiRequest($url, $requestType, $data) 106 | { 107 | return EGithub::githubRequest($url, $requestType, $data); 108 | } 109 | 110 | protected function populate($attributes) 111 | { 112 | $relations = $this->relations(); 113 | $attributeNames = $this->attributeNames(); 114 | foreach($attributes as $attribute => $value) 115 | { 116 | if (in_array($attribute, $attributeNames)) { 117 | $this->_attributes[$attribute] = $value; 118 | } 119 | elseif (isset($relations[$attribute])) 120 | { 121 | $className = $relations[$attribute][1]; 122 | switch ($relations[$attribute][0]) 123 | { 124 | case static::RELATION_ONE: 125 | $this->_related[$attribute] = new $className(); 126 | if (!($this->_related[$attribute] instanceof EGithubModel)) { 127 | throw new CException('GithubModel related class ' . $className . ' must be a subclass of EGithubModel.'); 128 | } 129 | $this->_related[$attribute]->populate($value); 130 | break; 131 | case static::RELATION_MANY: 132 | $this->_related[$attribute] = array(); 133 | if (!is_array($value) && !is_object($value)) { 134 | break; 135 | } 136 | //echo "
" . $attribute . ' := '. print_r($value,1) . '';die($className); 137 | foreach($value as $key => $relatedRecord) { 138 | $this->_related[$attribute][$key] = new $className(); 139 | if (!($this->_related[$attribute][$key] instanceof EGithubModel)) { 140 | throw new CException('GithubModel related class ' . $className . ' must be a subclass of EGithubModel.'); 141 | } 142 | $this->_related[$attribute][$key]->populate($relatedRecord); 143 | } 144 | break; 145 | default: 146 | throw new CException('Unknown GithubModel relation type specified for relation ' . $attribute . '.'); 147 | } 148 | } 149 | } 150 | } 151 | 152 | /** 153 | * PHP getter magic method. 154 | * This method is overridden so that attributes can be accessed like properties. 155 | * @param string $name property name 156 | * @return mixed property value 157 | */ 158 | public function __get($name) 159 | { 160 | if (isset($this->_attributes[$name])) { 161 | return $this->_attributes[$name]; 162 | // } else if(isset($this->_related[$name])) { 163 | // return $this->_related[$name]; 164 | } else { 165 | return parent::__get($name); 166 | } 167 | } 168 | 169 | /** 170 | * PHP setter magic method. 171 | * This method is overridden so that attributes can be accessed like properties. 172 | * @param string $name property name 173 | * @param mixed $value property value 174 | */ 175 | public function __set($name, $value) 176 | { 177 | if (in_array($name, $this->attributeNames())) { 178 | $this->_attributes[$name] = $value; 179 | } else { 180 | parent::__set($name, $value); 181 | } 182 | } 183 | 184 | /** 185 | * Checks if a property value is null. 186 | * This method overrides the parent implementation by checking 187 | * if the named attribute is null or not. 188 | * @param string $name the property name or the event name 189 | * @return boolean whether the property value is null 190 | */ 191 | public function __isset($name) 192 | { 193 | if (isset($this->_attributes[$name])) { 194 | return true; 195 | } else if (in_array($name, $this->attributeNames())) { 196 | return false; 197 | } else { 198 | return parent::__isset($name); 199 | } 200 | } 201 | 202 | /** 203 | * Sets a component property to be null. 204 | * This method overrides the parent implementation by clearing 205 | * the specified attribute value. 206 | * @param string $name the property name or the event name 207 | */ 208 | public function __unset($name) 209 | { 210 | if (isset($this->_attributes[$name])) { 211 | unset($this->_attributes[$name]); 212 | // } else if(isset($this->getMetaData()->relations[$name])) { 213 | // unset($this->_related[$name]); 214 | } else { 215 | parent::__unset($name); 216 | } 217 | } 218 | 219 | /** 220 | * Calls the named method which is not a class method. 221 | * Do not call this method. This is a PHP magic method that we override 222 | * to implement the named scope feature. 223 | * @param string $name the method name 224 | * @param array $parameters method parameters 225 | * @return mixed the method return value 226 | */ 227 | public function __call($name,$parameters) 228 | { 229 | if(isset($this->getMetaData()->relations[$name])) 230 | { 231 | if(empty($parameters)) 232 | return $this->getRelated($name,false); 233 | else 234 | return $this->getRelated($name,false,$parameters[0]); 235 | } 236 | 237 | $scopes=$this->scopes(); 238 | if(isset($scopes[$name])) 239 | { 240 | $this->getDbCriteria()->mergeWith($scopes[$name]); 241 | return $this; 242 | } 243 | 244 | return parent::__call($name,$parameters); 245 | } 246 | 247 | 248 | } 249 | 250 | class EGithubModelRequestException extends CException 251 | { 252 | private $_responseCode; 253 | 254 | public function getResponseCode() 255 | { 256 | return $this->_responseCode; 257 | } 258 | 259 | public function __construct($responseCode, $message) 260 | { 261 | $this->_responseCode = $responseCode; 262 | parent::__construct($message); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /models/EGithubUser.php: -------------------------------------------------------------------------------- 1 | array(static::RELATION_ONE, 'EGithubUserPlan'), 50 | ); 51 | } 52 | 53 | public function findScopes() 54 | { 55 | return array( 56 | 'find' => '/users' 57 | ); 58 | } 59 | 60 | public static function model($className=__CLASS__) 61 | { 62 | return parent::model($className); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /models/EGithubUserPlan.php: -------------------------------------------------------------------------------- 1 |