/** * Display output as html using a header and footer. * * @param array $parameters Output parameters to display * @param $VIEWER_page $page Page's file name * @param $VIEWER_title Page's title */ public function view($parameters) { $num_args = func_num_args(); if ($num_args > 1) { $VIEWER_page = func_get_arg(1); } if ($num_args > 2) { $VIEWER_title = func_get_arg(2); } if (!isset($VIEWER_page) || !$VIEWER_page) { //In case page parameter is not set $VIEWER_page = 'error'; } //copy title if not set as funtion argument and set in parameters if ((!isset($VIEWER_title) || !$VIEWER_title) && isset($parameters['title'])) { $VIEWER_title = $parameters['title']; } elseif (!isset($VIEWER_title)) { $VIEWER_title = ''; } $parameters['base'] = \Phramework\Phramework::getSetting('base'); $parameters['VIEWER_title'] = $VIEWER_title; $parameters['VIEWER_page'] = $VIEWER_page; extract($parameters); include __DIR__ . '/header.php'; //Include the page file include __DIR__ . '/pages/' . $VIEWER_page . '.php'; include __DIR__ . '/footer.php'; }
/** * Database \Exception * * @todo Notify administrators * @param string $message \Exception message * @param string $error Internal error message */ public function __construct($message, $error = null) { if (\Phramework\Phramework::getSetting('debug') && $error) { parent::__construct($error, 500); } else { parent::__construct($message, 500); } }
/** * Get url of the API resource. * * This method uses `api_base` setting to create the url. * @param string $endpoint [Optional] * @param string $suffix [Optional] Will append to the end of url * @return string Returns the created url */ public static function url($endpoint = null, $suffix = '') { $base = Phramework::getSetting('base'); if ($endpoint) { $suffix = $endpoint . '/' . $suffix; $suffix = str_replace('//', '/', $suffix); } return $base . $suffix; }
public static function GETSingle($params, $method, $headers) { $id = Request::requireId($params); $posts = Post::getAll(); array_unshift($posts, []); if ($id == 0 || $id > count($posts) - 1) { throw new \Phramework\Exceptions\NotFoundException('Post not found'); } Phramework::view(['posts' => [$posts[$id]]], 'blog', 'My blog #' . $id); //will load viewers/page/blog.php }
/** * Check if current request is authenticated * * Optionaly it checks the authenticated user has a specific user_id * @param integer $userId *[Optional]* Check if current user has the same id with $userId * @return object Returns the user object * @throws \Phramework\Exceptions\PermissionException * @throws \Phramework\Exceptions\UnauthorizedException */ public static function checkPermission($userId = false) { $user = \Phramework\Phramework::getUser(); //If user is not authenticated throw an \Exception if (!$user) { throw new \Phramework\Exceptions\UnauthorizedException(); } //Check if speficied user is same as current user if ($userId !== false && $user->id != $userId) { throw new PermissionException('Insufficient permissions'); } return $user; }
/** * Protected constructor to prevent creating a new instance of the * *Singleton* via the `new` operator from outside of this class. */ protected function __construct() { try { if (!self::$instance && class_exists('Memcached')) { self::$instance = new \Memcached(); self::$instance->addServer('localhost', 11211); if ($prefix = \Phramework\Phramework::getSetting('cache_prefix')) { self::$prefix = $prefix; } } } catch (\Exception $e) { self::$instance = null; } }
/** * Display output * * @param array $parameters Output parameters to display */ public function view($parameters) { if (!headers_sent()) { header('Content-Type: application/json;charset=utf-8'); } //If JSONP requested (if callback is requested though GET) if ($callback = \Phramework\Phramework::getCallback()) { echo $callback; echo '(['; echo json_encode($parameters); echo '])'; } else { echo json_encode($parameters); } }
public static function POST($params) { //Define model $model = ['title' => ['type' => Validate::TYPE_TEXT, 'max' => 12, 'min' => 3, Validate::REQUIRED], 'content' => ['type' => Validate::TYPE_TEXT, 'max' => 4096, 'min' => 12, Validate::REQUIRED]]; //Require and Validate model Validate::model($params, $model); //Declare them as variables $title = $params['title']; $content = $params['content']; $post = ['title' => $title, 'content' => $content, 'timestamp' => time()]; $post = \Phramework\Models\Filter::castEntry($post, ['timestamp' => Validate::TYPE_UNIX_TIMESTAMP]); //Store ($title, $content) somehow and get the id $id = rand(0, 100); $post['id'] = $id; \Phramework\Models\Response::created('http://localhost/post/' . $id . '/'); //Sample output Phramework::view(['post' => $post], 'post', 'Blog post'); }
/** * Send an e-mail * * @param string $address * @param string $subject * @param string $body * @param string $account *[Optional]*, Account name * @throws \Exception When email setting is not set */ public static function send($address, $subject, $body, $account = 'default') { $HTML = true; $accounts = \Phramework\Phramework::getSetting('email'); if (!$accounts || !isset($accounts['default'])) { throw new \Exception('email setting is required'); } if (!isset($accounts[$account])) { $account = 'default'; } $headers = []; $headers[] = "MIME-Version: 1.0" . "\r\n"; if (!$HTML) { $headers[] = 'Content-Type: text/plain;charset=utf-8' . "\r\n"; } else { $headers[] = 'Content-Type: text/html;charset=utf-8' . "\r\n"; } $headers[] = 'From: ' . $accounts[$account]['name'] . ' <' . $accounts[$account]['mail'] . '>' . "\r\n"; $headers[] = 'Reply-To: ' . $accounts[$account]['name'] . ' <' . $accounts[$account]['mail'] . "\r\n"; mail($address, $subject, $body, implode('', $headers), '-f' . $accounts[$account]['mail']); }
public static function prepare() { if (static::$adapter !== null) { return; } $dbSettings = \Phramework\Phramework::getSetting('query-log', 'database'); if (!$dbSettings) { throw new \Phramework\Exceptions\ServerException('query-log.database is not configured'); } if (is_array($dbSettings)) { $dbSettings = (object) $dbSettings; } $adapterNamespace = $dbSettings->adapter; $adapter = new $adapterNamespace((array) $dbSettings); if (!$adapter instanceof \Phramework\Database\IAdapter) { throw new \Exception(sprintf('Class "%s" is not implementing Phramework\\Database\\IAdapter', $adapterNamespace)); } if (isset($dbSettings->schema)) { static::$schema = $dbSettings->schema; } static::$table = isset($dbSettings->table) ? $dbSettings->table : 'query_log'; static::setAdapter($adapter); }
/** * Invoke phramework causing NotFoundException * @covers Phramework\SystemLog\SystemLog::register */ public function testRegisterOnException() { $this->setUp(); //Force URI route $_SERVER['REQUEST_URI'] = '/not_found/'; $additionalParameters = (object) ['API' => 'phpunit']; $this->systemLog->register($additionalParameters); $that = $this; //Use a callback to copy $step and $object arguments from ILog to this PHPUnit object \Phramework\SystemLog\APP\Log\PHPUnit::setCallback(function ($step, $object) use($that) { $that->step = $step; $that->object = $object; }); //Invoke phramework (start test) $this->phramework->invoke(); /** * Use step and object for PHPUnit tests */ $this->assertSame($this->step, StepCallback::STEP_ERROR, 'Expect STEP_AFTER_CALL_URISTRATEGY, since we dont expect any exception'); $this->assertInternalType('object', $this->object); $this->assertInternalType('integer', $this->object->request_timestamp); $this->assertInternalType('integer', $this->object->response_timestamp); $this->assertGreaterThanOrEqual($this->object->request_timestamp, $this->object->response_timestamp, 'Response timestamp must be greater or equal to request timestamp'); //Expect required keys foreach (SystemLogTest::$objectKeys as $key) { $this->assertObjectHasAttribute($key, $this->object, 'Object must have key' . $key); } $this->assertNotNull($this->object->errors, 'Must not be null, since we expect exception'); $this->assertNotNull($this->object->exception, 'Must not be null, since we expect exception'); $this->assertObjectHasAttribute('API', $this->object->additional_parameters, 'Check if additional_parameters "API" passed in object'); $this->assertSame($additionalParameters->API, $this->object->additional_parameters->API, 'Check if value of additional_parameters "API" is set correctly'); //$that->>assertEquals( // 404, // $this->object->response_status_code, // 'Status code MUST be 404, since we expect exception, caused by NotFoundException' //); }
/** * Prepare log object * @param integer $flags * @param object $settings * @param object $params * @param string $HTTPMethod * @param array $headers * @param object|null $additionalParameters * @return object */ private static function prepareObject($flags, $settings, $params, $HTTPMethod, $headers, $additionalParameters) { list($URI) = \Phramework\URIStrategy\URITemplate::URI(); $object = (object) ['request_id' => Phramework::getRequestUUID(), 'URI' => $URI, 'method' => $HTTPMethod, 'user_id' => null, 'ip_address' => \Phramework\Models\Util::getIPAddress(), 'request_headers' => null, 'request_params' => null, 'request_body_raw' => null, 'request_timestamp' => $_SERVER['REQUEST_TIME'], 'response_timestamp' => time(), 'response_headers' => null, 'response_body' => null, 'response_status_code' => http_response_code(), 'exception' => null, 'exception_class' => null, 'errors' => null, 'call_trace' => null, 'flags' => $flags, 'additional_parameters' => $additionalParameters]; if (($flags & self::LOG_USER_ID) !== 0) { $user = Phramework::getUser(); $object->user_id = $user ? $user->id : false; } /* Request flags */ if (($flags & self::LOG_REQUEST_HEADERS) !== 0) { //Asterisk authorization header value except schema if (isset($headers['Authorization'])) { list($authorizationSchema) = sscanf($headers['Authorization'], '%s %s'); $headers['Authorization'] = $authorizationSchema . ' ***'; } $object->request_headers = $headers; } else { $request_headers = []; if (($flags & self::LOG_REQUEST_HEADER_CONTENT_TYPE) !== 0) { //Write content type $request_headers[\Phramework\Models\Request::HEADER_CONTENT_TYPE] = isset($headers[\Phramework\Models\Request::HEADER_CONTENT_TYPE]) ? $headers[\Phramework\Models\Request::HEADER_CONTENT_TYPE] : null; } if (($flags & self::LOG_REQUEST_HEADER_AGENT) !== 0) { $request_headers['User-Agent'] = isset($headers['User-Agent']) ? $headers['User-Agent'] : null; } if (($flags & self::LOG_REQUEST_HEADER_REFERER) !== 0) { $request_headers['Referer'] = isset($headers['Referer']) ? $headers['Referer'] : null; } if (($flags & self::LOG_REQUEST_HEADER_ACCEPT) !== 0) { $request_headers['Accept'] = isset($headers['Accept']) ? $headers['Accept'] : null; } if (!empty($request_headers)) { $object->request_headers = $request_headers; } } if (($flags & self::LOG_REQUEST_PARAMS) !== 0) { $object->request_params = $params; } if (($flags & self::LOG_REQUEST_BODY_RAW) !== 0) { $bodyRaw = file_get_contents('php://input'); if (strlen($bodyRaw) > $settings->body_raw_limit) { $bodyRaw = 'TRIMMED' . PHP_EOL . substr($bodyRaw, 0, $settings->body_raw_limit); } //Apply FILTER_SANITIZE_STRING $object->request_body_raw = \Phramework\Models\Filter::string($bodyRaw); //include content type headers if disabled if (!empty($bodyRaw) && ($flags & self::LOG_REQUEST_HEADERS) === 0 && ($flags & self::LOG_REQUEST_HEADER_CONTENT_TYPE) === 0) { $contentType = isset($headers[\Phramework\Models\Request::HEADER_CONTENT_TYPE]) ? $headers[\Phramework\Models\Request::HEADER_CONTENT_TYPE] : null; if (empty($object->request_headers)) { //make sure it's array $object->request_headers = []; } $object->request_headers[\Phramework\Models\Request::HEADER_CONTENT_TYPE] = $contentType; } } $responseHeaders = new \stdClass(); foreach (headers_list() as $header) { list($key, $value) = explode(': ', $header); $responseHeaders->{$key} = $value; } /* Response flags */ if (($flags & self::LOG_RESPONSE_HEADER) !== 0) { $object->response_headers = $responseHeaders; } if (($flags & self::LOG_RESPONSE_BODY) !== 0) { $object->response_body = ob_get_contents(); if (($flags & self::LOG_RESPONSE_HEADER) === 0) { //show content type if headers are disabled $object->response_headers = (object) [\Phramework\Models\Request::HEADER_CONTENT_TYPE => isset($responseHeaders->{\Phramework\Models\Request::HEADER_CONTENT_TYPE}) ? $responseHeaders->{\Phramework\Models\Request::HEADER_CONTENT_TYPE} : null]; } } return $object; }
public static function GETById($params, $method, $headers, $id) { \Phramework\Phramework::view(['data' => ['type' => 'dummy', 'id' => $id]]); }
/** * Shortcut to \Phramework\Phramework::view * @param array $params * @uses \Phramework\Phramework::view */ protected static function view($params = []) { \Phramework\Phramework::view($params); }
/** * Authenticate a user using JWT authentication method * @param array $params Request parameters * @param string $method Request method * @param array $headers Request headers * @return false|array Returns false on failure */ public function authenticate($params, $method, $headers) { //Require email and password set in params $validationModel = new \Phramework\Validate\ObjectValidator(['email' => new \Phramework\Validate\EmailValidator(3, 100), 'password' => new \Phramework\Validate\StringValidator(3, 128, null, true)], ['email', 'password']); $parsed = $validationModel->parse($params); $email = $parsed->email; $password = $parsed->password; //Get user object $user = call_user_func(Manager::getUserGetByEmailMethod(), $email); if (!$user) { return false; } // Verify user's password (password is stored as hash) if (!password_verify($password, $user['password'])) { return false; } $secret = Phramework::getSetting('jwt', 'secret'); $algorithm = Phramework::getSetting('jwt', 'algorithm'); $serverName = Phramework::getSetting('jwt', 'server'); $tokenId = base64_encode(\mcrypt_create_iv(32)); $issuedAt = time(); $notBefore = $issuedAt + Phramework::getSetting('jwt', 'nbf', 0); $expire = $notBefore + Phramework::getSetting('jwt', 'exp', 3600); /* * Create the token as an array */ $data = ['iat' => $issuedAt, 'jti' => $tokenId, 'iss' => $serverName, 'nbf' => $notBefore, 'exp' => $expire, 'data' => ['id' => $user['id']]]; //copy user attributes to jwt's data foreach (Manager::getAttributes() as $attribute) { if (!isset($user[$attribute])) { throw new \Phramework\Exceptions\ServerException(sprintf('Attribute "%s" is not set in user object', $attribute)); } $data['data'][$attribute] = $user[$attribute]; } $jwt = \Firebase\JWT\JWT::encode($data, $secret, $algorithm); //Call onAuthenticate callback if set if (($callback = Manager::getOnAuthenticateCallback()) !== null) { call_user_func($callback, (object) $data['data'], $jwt); } return [(object) $data['data'], $jwt]; }
/** * Invoke URIStrategy * @param object $requestParameters Request parameters * @param string $requestMethod HTTP request method * @param array $requestHeaders Request headers * @param object|false $requestUser Use object if successful * authenticated otherwise false * @throws Phramework\Exceptions\NotFoundException * @throws Phramework\Exceptions\UnauthorizedException * @throws Phramework\Exceptions\ServerException * @return string[2] This method should return `[$class, $method]` on success */ public function invoke(&$requestParameters, $requestMethod, $requestHeaders, $requestUser) { //Get controller from the request (URL parameter) if (!isset($requestParameters['controller']) || empty($requestParameters['controller'])) { if ($defaultController = Phramework::getSetting('default_controller')) { $requestParameters['controller'] = $defaultController; } else { throw new \Phramework\Exceptions\ServerException('Default controller has not been configured'); } } $controller = $requestParameters['controller']; unset($requestParameters['controller']); //Check if requested controller and method are allowed if (!in_array($controller, $this->controllerWhitelist)) { throw new NotFoundException('Method not found'); } elseif (!in_array($requestMethod, Phramework::$methodWhitelist)) { throw new \Phramework\Exceptions\MethodNotAllowedException('Method not found'); } //If not authenticated allow only certain controllers to access if (!$requestUser && !in_array($controller, $this->controllerUnauthenticatedWhitelist) && !in_array($controller, $this->controllerPublicWhitelist)) { throw new \Phramework\Exceptions\UnauthorizedException(); } // Append suffix $controller = $controller . ($this->suffix ? $this->suffix : ''); /** * Check if the requested controller and model is callable * In order to be callable : * 1) The controllers class must be defined as : myname_$suffix * 2) the methods must be defined as : public static function GET($requestParameters) * where $requestParameters are the passed parameters */ if (!is_callable($this->namespace . "{$controller}::{$requestMethod}")) { //Retry using capitalized first letter of the class $controller = ucfirst($controller); if (!is_callable($this->namespace . "{$controller}::{$requestMethod}")) { throw new NotFoundException('Method not found'); } } //Call handler method call_user_func([$this->namespace . $controller, $requestMethod], $requestParameters, $requestMethod, $requestHeaders); return [$controller, $requestMethod]; }
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //Show all errors error_reporting(E_ALL); ini_set('display_errors', '1'); //This autoload path is for loading current version of phramework require __DIR__ . '/../vendor/autoload.php'; //define controller namespace, as shortcut define('NS', '\\Phramework\\Examples\\API\\Controllers\\'); use Phramework\Phramework; /** * @package examples/post * Define APP as function */ $APP = function () { //Include settings $settings = (include __DIR__ . '/../settings.php'); $URIStrategy = new \Phramework\URIStrategy\URITemplate([['book/', NS . 'BookController', 'GET', Phramework::METHOD_GET], ['book/{id}', NS . 'BookController', 'GETSingle', Phramework::METHOD_GET], ['book/', NS . 'BookController', 'POST', Phramework::METHOD_POST]]); //Initialize API $phramework = new Phramework($settings, $URIStrategy); unset($settings); Phramework::setViewer(\Phramework\Viewers\JSON::class); //Execute API $phramework->invoke(); }; /** * Execute APP */ $APP();
/** * Log query to database * @param string $query * @param array $parameters * Query parameters * @param integer $startTimestamp * Timestamp before query was executed * @param null|Exception $exception * *[Optional]* Exception object if any */ protected function log($query, $parameters, $startTimestamp, $exception = null) { $endTimestamp = time(); $duration = $endTimestamp - $startTimestamp; $user = \Phramework\Phramework::getUser(); $user_id = $user ? $user->id : null; //Get request URI list($URI) = \Phramework\URIStrategy\URITemplate::URI(); //Get request method $method = \Phramework\Phramework::getMethod(); $debugBacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); //Function used by database adapter $adapterFunction = $debugBacktrace[1]['function']; //Remove current log function call //Remove QueryLogAdapter execute* function call array_splice($debugBacktrace, 0, 2); foreach ($debugBacktrace as $k => &$v) { if (isset($v['class'])) { $class = $v['class']; $function = $v['function']; //Check if matrix has an entry for this class if (property_exists($this->matrix, $class)) { $matrixEntry = $this->matrix->{$class}; if (is_object($matrixEntry) || is_array($matrixEntry)) { //If vector, then is vector contains values for multiple methods of this class //Work with objects if (is_array($matrixEntry)) { $matrixEntry = (object) $matrixEntry; } if (property_exists($matrixEntry, $function)) { //If non positive value, dont log current query if (!$matrixEntry->{$function}) { return self::LOG_INGORED; } } } else { //scalar, this entry has a single value for all methods of this class //If non positive value, dont log current query if (!$matrixEntry) { return self::LOG_INGORED; } } } $v = $v['class'] . '::' . $v['function']; } else { $v = $v['function']; } } $schemaTable = $this->schema ? '"' . $this->schema . '"."' . $this->table . '"' : '"' . $this->table . '"'; //Insert query log record into table return $this->logAdapter->execute('INSERT INTO ' . $schemaTable . '( "request_id", "query", "parameters", "start_timestamp", "duration", "function", "URI", "method", "additional_parameters", "call_trace", "user_id", "exception" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [\Phramework\Phramework::getRequestUUID(), $query, $parameters ? json_encode($parameters) : null, $startTimestamp, $duration, $adapterFunction, $URI, $method, $this->additionalParameters ? json_encode($this->additionalParameters) : null, json_encode($debugBacktrace), $user_id, $exception ? serialize(QueryLog::flattenExceptionBacktrace($exception)) : null]); }
/** * Type cast entry's attributes based on the provided model * * If any TYPE_UNIX_TIMESTAMP are present an additional attribute will * be included with the suffix _formatted, the format of the string can be * changed from timestamp_format setting. * @param array $entry * @param array $model * @return array Returns the typecasted entry * @deprecated since 1.1.0 */ public static function castEntry($entry, $model) { if (!$entry) { return $entry; } $timestamp_format = \Phramework\Phramework::getSetting('timestamp_format', null, 'Y-m-d\\TH:i:s\\Z'); //Repeat for each model's attribute of the entry. //$k holds the key of the attribute and $v the type foreach ($model as $k => $v) { if (!isset($entry[$k])) { continue; } //Typecast Filter::typecast($entry[$k], $v); //if type is a Validate::TYPE_UNIX_TIMESTAMP //then inject a string version of the timestamp to this entry if ($v === Validate::TYPE_UNIX_TIMESTAMP) { //offset included! $converted = gmdate($timestamp_format, $entry[$k]); //inject the string version of the timestamp $entry[$k . '_formatted'] = $converted; } } return $entry; }
/** * Add a step callback * * Step callbacks, are callbacks that executed when the API reaches * a certain step, multiple callbacks can be set for the same step. * @param string $step * @param function $callback * @since 0.1.1 * @throws Exception When callback is not not callable * @throws Phramework\Exceptions\IncorrectParametersException */ public function add($step, $callback) { //Check if step is allowed (new \Phramework\Validate\EnumValidator([self::STEP_BEFORE_AUTHENTICATION_CHECK, self::STEP_AFTER_AUTHENTICATION_CHECK, self::STEP_AFTER_CALL_URISTRATEGY, self::STEP_BEFORE_CALL_URISTRATEGY, self::STEP_BEFORE_CLOSE, self::STEP_FINALLY, self::STEP_ERROR]))->parse($step); if (!is_callable($callback)) { throw new \Exception(Phramework::getTranslated('Callback is not callable')); } //If stepCallback list is empty if (!isset($this->stepCallback[$step])) { //Initialize list $this->stepCallback[$step] = []; } //Push $this->stepCallback[$step][] = $callback; }