In an instance such as the following which is called?
This is our base class
<?php
class baseService
{
public function __construct()
{
//constructor code
}
public function handleRawRequest($_SERVER, $_GET, $_POST)
{
$this->handleRequest($url, $method, $parameters, $requestBody, $accept);
}
public function handleRequest($url, $method, $parameters, $requestBody, $accept)
{
switch($method)
{
case 'GET':
$this->performGet($url, $parameters, $requestBody, $accept);
break;
default:
$this->notImplementedResponse();
}
}
public function performGet($url, $parameters, $requestBody, $accept)
{
$this->methodNotAllowedResponse();
}
protected function methodNotAllowedResponse()
{
// out put some sort of 405 (Method Not Allowed)
}
}
?>
We extend this to a specific service to provide certain functionality
<?php
require "baseService.php";
class betterService extends baseService
{
public function __construct()
{
// stuff
}
public function performGet($url, $parameters, $requestBody, $accept)
{
//do some stuff
}
}
?>
Then using a another php file we create a better service and send some requests to it like such:
<?php
require "NameRestService.php";
$service = new NameRestService();
$service->handleRawRequest($_SERVER, $_GET, $_POST);
?>
Do we get to do some stuff or is the method not allowed message returned?
If it is indeed the second how can it be changed to do stuff?
replace $this by self:: to execute the method from the calling class.
self::performGet(...
Related
I am very new to PHP (did some small projects a few years back, but nothing since) and trying to get familiar with it again by creating a RESTful API project. Coming from a C# background, I'm trying to setup the boiler plate stuff to be similar to that of .NET, where controllers handle the request and attributes help determine which function should handle the request.
The problem I'm running into is retrieving the attributes from a method within the Controller class. I create an instance of ReflectionClass, call getMethods() then attempt to iterate the methods to retrieve the attributes by calling $method->getAttributes(HttpGet::class) but the $method->getAttributes() call is throwing the following error
'PHP message: PHP Fatal error: Uncaught Error: Call to undefined method ReflectionMethod::getAttributes() in /var/www/*********/api/base/ApiController.php:36\
Here is the code
-Running on Apache2 / Linux with PHP 7.4.28
The Controller
require './base/ApiController.php';
require './base/attributes/RouteAttributes.php';
class SessionController extends ApiController
{
#[HttpGet('')]
public function OnGet()
{
parent::OK("GET Called - Returned 200 OK");
}
#[HttpPost('')]
public function OnPost()
{
parent::Created("POST called - Returned 201 CREATED");
}
}
$controller = new SessionController();
ApiController::HandleRequest($controller);
?>
The ApiController base class
/* Base class for all API controllers, provides basic functionality like
* returning common statuses, conversion of data to JSON and formatting
* appropriate headers
*/
require "HttpStatusCode.php";
class ApiController
{
//Return Types
public static function StatusCode(int $status, $data)
{
header('X-PHP-Response-Code: $status->value', true, $status);
if(isset($data))
{
header("Content-type: application/json");
echo json_encode($data);
}
exit;
}
public static function HandleRequest($controller)
{
$routePath = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : NULL;
$requestMethod = strtoupper($_SERVER['REQUEST_METHOD']);
$controllerInfo = new ReflectionClass($controller);
$methodFound = false;
switch($requestMethod)
{
case 'GET':
foreach($controllerInfo->getMethods() as $method)
{
echo $method;
$getAttribute = $method->getAttributes(HttpGet::class);
if(count($getAttribute))
{
$method.invoke($controller, NULL);
$methodFound = true;
}
}
break;
case 'POST':
break;
}
if($methodFound == false)
ApiController::StatusCode(HttpStatusCode::NotFound, "Method Not Found");
}
public function OK($data)
{
ApiController::StatusCode(HttpStatusCode::OK, $data);
}
public function Created($data, $createdAtRoute)
{
if(isset($createdAtRoute))
header('Location: $createdAtRoute');
StatusCode(HttpStatusCode::Created, $data);
}
public function Accepted($data)
{
StatusCode(HttpStatusCode::Accepted, $data);
}
public function NoContent()
{
StatusCode(HttpStatusCode::NoContent, NULL);
}
public function BadRequest($data)
{
StatusCode(HttpStatusCode::BadRequest, $data);
}
public function Unathorized($data)
{
StatusCode(HttpStatusCode::Unathorized, $data);
}
public function Forbidden($data)
{
StatusCode(HttpStatusCode::Forbidden, $data);
}
}
?>
The Attribute definitions
#[Attribute]
class HttpGet
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpPost
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpPut
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpPatch
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpDelete
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
?>
The project is very much still in the infancy stages and going to be a functional 'learner' project to get my feet wet. I just haven't been able to get past this error. Any help or insight would be greatly appreciated.
Your issue is with the PHP version. PHP Annotations have been supported since PHP 8.0
Not sure why but Its not even hitting the var_dump() that I have. Lets look at how I have it implemented.
<?php
namespace ImageUploader\Controllers;
class ApplicationController implements \Lib\Controller\BaseController {
....
public function beforeAction($actionName = null, $actionArgs = null){}
public function afterAction($actionName = null, $actionArgs = null){}
public static function __callStatic($name, $args) {
var_dump('hello?'); exit;
if (method_exists($this, $name)) {
$this->beforeAction($name, $args);
$action = call_user_func(array($this, $name), $args);
$this->afterAction($name, $args);
return $action;
}
}
}
As we can see I want to do something before and after an action is called, regardless if you implemented the method or not. But that var_dump is never reached.
This class is extended in:
<?php
namespace ImageUploader\Controllers;
use \Freya\Factory\Pattern;
class DashboardController extends ApplicationController {
public function beforeAction($actionName = null, $actionArgs = null) {
var_dump($actionName, $actionArgs); exit;
}
public static function indexAction($params = null) {
Pattern::create('\Freya\Templates\Builder')->renderView(
'dash/home',
array(
'flash' => new \Freya\Flash\Flash(),
'template' => Pattern::create('\Freya\Templates\Builder')
)
);
}
....
}
Now when I do: DashboardController::indexAction(); it should exit ... unless I am missing something. If that's the case - what is it?
even the var_dump in the before_action(...) that's implemented is never reached (obvi' because of the first one, but if I take out the first one the second is never reach.)
__callStatic is called only when a static method does not exist - as indexAction actually is defined, it is executed without bothering __callStatic(). (Documentation)
An approach to achieve what you are trying to do could be by wrapping your controller inside a decorator:
class ExtendedApplicationController
{
/**
* #var \Lib\Controller\BaseController
*/
protected $controller;
function __construct(\Lib\Controller\BaseController $controller) {
$this->controller = $controller;
}
function __callStatic($name, $args) {
if (method_exists($this->controller, 'beforeAction')) {
call_user_func_array(array($this->controller, 'beforeAction'), $name, $args);
}
call_user_func_array(array($this->controller, $name), $args);
if (method_exists($this->controller, 'afterAction')) {
call_user_func_array(array($this->controller, 'afterAction'), $name, $args);
}
}
}
and then, in your code, you could do:
$controller = new ExtendedApplicationController(new DashboardController());
$controller::indexAction();
I have to warn you that I didn't test this approach while I was writing it, but I hope it gives you an idea!
basically I'm using PHP Overloading to create dynamic methods and properties. What I want to do is to trigger a function to a property access but keeping the access to its methods.
In other terms, that's my PHP code:
First Class:
class _class {
private $_instance;
public function __construct() {
$this->_instance = new _object();
}
public function __get($name) {
switch ($name) {
case "instance":
//LOGICS
break;
}
return null;
}
}
Second Class:
class _object {
public function __call($method, $args) {
switch ($method) {
case "method":
//LOGICS
break;
}
return null;
}
}
Now I want to execute a function when I access to an object property in this way:
$obj = new _class();
echo $obj->instance; //some output here, executing a function
echo $obj->instance->method(); //different output, executing the method of the instance
Thanks, an help will be really appreciated!
When you instantiate the first class, then you create a $this->_instance instead $obj->instance. So $obj->instance will be null and can not call anything on that.
But, if you try $obj->_instance->method();, that will be bad also, because _instance is private. So you need to add getter. Try this:
class _class {
private $_instance;
public function __construct() {
$this->_instance = new _object();
}
public function __get($name) {
switch ($name) {
case "instance":
echo "Instance";
break;
}
return null;
}
public function getInstance() {
return $this->_instance;
}
}
class _object {
public function __call($method, $args) {
switch ($method) {
case "method":
echo "Method";
break;
}
return null;
}
}
$obj = new _class();
$obj->instance;
$obj->getInstance()->method();
Output is:
Instance
Method
I am creating a little system which will allow users to extend the system with their own classes.
Class Core {
static $confArray;
static $extendArray;
protected static $instance;
public function read($name)
{
return self::$confArray[$name];
}
public function put($name, $value)
{
self::$confArray[$name] = $value;
}
public function extend($function, $handler, $args=null){
self::$extendArray[$function] = new $handler($args);
}
public function __call($method, $args){
return self::$extendArray[$method];
}
public static function getInstance()
{
if (!isset(self::$instance))
{
$object =__CLASS__;
self::$instance= new $object;
}
return self::$instance;
}
}
With That, now a user can come and register a simple extension from such a class:
class WorkersTest{
function isWorking($who){
echo "$who is working";
}
function isNotWorking($who){
echo "$who is not working";
}
}
To call the function (isworking/ isNotWorking), a the programmer needs to register the test class through:
Core::getInstance->extend("worker","WorkersTest");
Then it can now be called through:
Core::getInstance->worker()->isWorking("George");
This is working perfectly. My question is how i can remove the () in the call (dont worry why) and have:
Core::getInstance->worker->isWorking("George");
Is it possible?
You can use the magic __get() method, just like __call():
public function __get($name)
{
return $this->$name();
}
Try overriding the magic __get() method to return what you need:
Class Core {
// (...)
public function __get($name) {
if (isset( self::$extendArray[$function] )) {
return $this->$name();
}
//if there is no function registered under named "$name"
//throwing Exception is by design better, as #scragar suggested
throw new Exception("No function registered under named {$name}");
//return NULL;
}
}
I am creating a rest service. I have done all the required methods, what I want to do is some authorization. I have created the table where I store the api-keys, i load them in each method, and it works quite well.
What I need now, is to do some before action that would be called before each method, so i don't have to check if the user is successfully authorized on each method? In simple CI_Controller or in FuelPHP that can be done using public function before, but I dont know how to achieve the same thing in REST_Controller?
Thank you in advance
Here are two controllers. May give you some idea
class MY_Controller extends CI_Controller
{
protected $before_filter = array();
protected $after_filter = array();
private function run_filter($who, $params=array())
{
$filter = $this->{"{$who}_filter"};
if (is_string($filter)) {
$filter = array($filter);
}
if (method_exists($this, "{$who}_filter")) {
$filter[] = "{$who}_filter";
}
foreach ($filter as $method) {
call_user_func_array(array($this, $method), $params);
}
}
public function _remap($method, $parameters)
{
if (method_exists($this, $method))
{
$this->run_filter('before', $parameters);
$return = call_user_func_array(array($this, $method),$parameters);
$this->run_filter('after', $parameters);
}else{
show_404();
}
}
}
class MY_Controller extends CI_Controller
{
public $before_filter = array('check_login');
public $after_filter = array();
private function dashboard()
{
/* other code here */
}
public function check_login()
{
/* Login checking Code here */
}
}