PHP: ReflectionMethod::getAttributes throwing error 'Call to undefined method' - php

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

Related

Class autoload not working

I'm a beginner in PHP development and I'm facing a problem in my development in PHP OO. I saw is better use the autoload() function than include each file of PHP Class.
My doubt is: Why my autoload function does not work?
Follow bellow my code:
<?php
function __autoload($class)
{
include_once "model/{$class}.class.php";
}
$avaliacaoLocal = new AvaliacaoLocal();
$avaliacaoLocal->setId(1);
$avaliacaoLocal->setIdLocal(2);
$avaliacaoLocal->setComentarios("Comentários de Pedro");
$avaliacaoLocal->setIdPessoaCliente(3);
$avaliacaoLocal->setValor(5);
var_dump($avaliacaoLocal);
File AvaliacaoLocal.class.php
<?php
namespace model;
class AvaliacaoLocal
{
private $id;
private $valor;
private $comentarios;
private $idLocal;
private $idPessoaCliente;
public function __construct(){
$this->clear();
}
public function clear(){
$this->id = 0;
$this->valor = 0;
$this->comentarios = "";
$this->idLocal = null;
$this->idPessoaCliente = null;
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getValor()
{
return $this->valor;
}
public function setValor($valor)
{
$this->valor = $valor;
}
public function getComentarios()
{
return $this->comentarios;
}
public function setComentarios($comentarios)
{
$this->comentarios = $comentarios;
}
public function getIdLocal()
{
return $this->idLocal;
}
public function setIdLocal($idLocal)
{
$this->idLocal = $idLocal;
}
public function getIdPessoaCliente()
{
return $this->idPessoaCliente;
}
public function setIdPessoaCliente($idPessoaCliente)
{
$this->idPessoaCliente = $idPessoaCliente;
}
}
The error:
PHP Fatal error: Class 'AvaliacaoLocal' not found in C:\Users\Pedro
........\index.php on line 14
UPDATE:
When i use include the PHP returns the same error:
Fatal error: Class 'AvaliacaoLocal' not found in C:\Program
Files\VertrigoServ\www\system\index.php on line 10
i've change folder to verify if could be it.
The class is declared belonging to a namespace, you have to call it in this way:
$avaliacaoLocal = new \model\AvaliacaoLocal();
But now, the namespace is also included in $class, so the autoload function needs to handle that:
function __autoload($class)
{
$file = str_replace(array('_', '\\'), '/', $class) . '.php';
if (is_file($file)) {
require $file;
}
}
This function takes $class value and replace every \ (and _) from the namespace with a / to get the file name.

Overriding a function in base class php

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(...

Google Maps Engine API Hello World Example in PHP causing MapItem not found

I am trying to work through getting the PHP client for Google Map to work correctly.
I've downloaded a local copy of the GoogleAPI PHP Client from GitHub:https://github.com/google/google-api-php-client.
I am running PHP v5.4 on IIS8. The GoogleAPI was installed in the PHP Include folder, under GoogleAPI.
PHP works correctly with all my other scripts.
I am trying get the example to work from Maps-Engine Documentation.
<?php
ini_set('display_errors','on');
require('GoogleAPI/autoload.php');
//require_once 'GoogleAPI/src/Google/Client.php';
//require_once 'Google/Service/MapsEngine.php';
$apiKey = "API Key";
$client = new Google_Client();
$client->setApplicationName("Google-PhpMapsEngineSample/1.0");
$client->setDeveloperKey($apiKey);
$service = new Google_Service_MapsEngine($client);
$optParams = array('maxResults' => 500, 'version' => 'published');
$results = $service->tables_features->listTablesFeatures("12421761926155747447-06672618218968397709", $optParams);
print_r($results);
?>
The only changes to the code example were the API Key, load the Google Autoloader and comment out the require_once directives.
The output I receive is:
Fatal error: Class 'Google_Service_MapsEngine_MapItem' not found in C:\Program Files (x86)\PHP\v5.4\includes\GoogleAPI\src\Google\Service\MapsEngine.php on line 4702
MapsEngine:4702 extends the Google_Service_MapsEngine_MapItem class. The Google_Service_MapsEngine_MapItem class extends the Google_Model class defined in Model.php file.
Hi I had the same problem.
There is a bug in the google-api-php-client/src/Google/Service/MapsEngine.php file. The class Google_Service_MapsEngine_MapFolder which exends the Google_Service_MapsEngine_MapItem is declared before the class Google_Service_MapsEngine_MapItem is declared.
I switch the order of the 2 classes in the MapsEngine.php file and that fixed the problem. This shows the correct order for the classes.
class Google_Service_MapsEngine_MapItem extends Google_Model
{
protected $internal_gapi_mappings = array(
);
public $type;
public function setType($type)
{
$this->type = $type;
}
public function getType()
{
return $this->type;
}
}
class Google_Service_MapsEngine_MapFolder extends Google_Service_MapsEngine_MapItem
{
protected $collection_key = 'defaultViewport';
protected $internal_gapi_mappings = array(
);
protected $contentsType = 'Google_Service_MapsEngine_MapItem';
protected $contentsDataType = 'array';
public $defaultViewport;
public $expandable;
public $key;
public $name;
public $visibility;
protected function gapiInit()
{
$this->type = 'folder';
}
public function setContents($contents)
{
$this->contents = $contents;
}
public function getContents()
{
return $this->contents;
}
public function setDefaultViewport($defaultViewport)
{
$this->defaultViewport = $defaultViewport;
}
public function getDefaultViewport()
{
return $this->defaultViewport;
}
public function setExpandable($expandable)
{
$this->expandable = $expandable;
}
public function getExpandable()
{
return $this->expandable;
}
public function setKey($key)
{
$this->key = $key;
}
public function getKey()
{
return $this->key;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setVisibility($visibility)
{
$this->visibility = $visibility;
}
public function getVisibility()
{
return $this->visibility;
}
}

PHP MVC - Why is my request/router always going to home controller?

I've tried many fixes but for some reason my request and routing mechanisms in my framework aren't working properly. It's a multi-language site (en/fr) and I use mod_rewrite to get the language, class, method and an optional argument. My request.php sets the controller to languageController if the language isn't set, and it works (successfully echo "language" in controller). But, if the language is set, the controller that always ends up displaying is homeController despite that the uri is /en/about. I have no idea why this is happening and it's really frustrating me. Here's the problem code, help is appreciated.
Note: I haven't used namespaces as it is a small website that I just want to get up and running and there's no real need, although I plan to edit it to use namespaces in the future simply for the leaning.
relevant parts of index.php
//analyze request
$request = new Request($_GET); //breaks up the uri into an array
//routing
$router = new Router($request); //stores the requested uri in the router
$router->route(); //sets the proper route
//instancialize and execute the controller
$class = $router->getController();
$method = $router->getMethod();
$controller = new $class;
$controller->$method();
Request.php
class Request {
private $request;
public function __construct($uri) {
$this->request = $uri;
//if the language isn't selected
if($this->request['language']=="") {
$this->request['language'] = "language";
$this->request['class'] = "language";
$this->request['method'] = "index";
}
//else if the controller is not selected
else if($this->request['controller']=="") {
$this->request['class'] = "home";
$this->request['method'] = "index";
}
//stores the requested method
else if($this->request['method']=="") {
$this->request['method'] = "index";
}
//stores a value of false if there is no argument in the uri
if($this->request['arg']=="") {
$this->request['arg'] = false;
}
}
public function getLanguage() {
return $this->request['language'];
}
public function getClass() {
return $this->request['class'];
}
public function getMethod() {
return $this->request['method'];
}
public function getArgument() {
return $this->request['arg'];
}
}
Router.php
class Router {
private $language;
private $controller;
private $view;
private $method;
private $arg;
public function __construct(Request $request) { //given an instance of request, store it in local variables
$this->language = $request->getLanguage();
$this->controller = $request->getClass() . 'Controller';
$this->view = $request->getClass() . 'View';
$this->method = $request->getMethod();
$this->arg = $request->getArgument();
}
public function route() { //put the requested uri into forms we can deal with
if($this->language == "fr") { //compare it against url translation chart
//run all uri values through a chart that translates them from french to english if it finds a match
}
else { //it must be english
}
}
public function getLanguage() {
return $this->language;
}
public function getController() {
return $this->controller;
}
public function getView() {
return $this->view;
}
public function getMethod() {
return $this->method;
}
public function getArgument() {
return $this->arg;
}
}

PHP How to distinguish $this pointer in the inheritance chain?

Please look at the following code snipped
class A
{
function __get($name)
{
if ($name == 'service') {
return new Proxy($this);
}
}
function render()
{
echo 'Rendering A class : ' . $this->service->get('title');
}
protected function resourceFile()
{
return 'A.res';
}
}
class B extends A
{
protected function resourceFile()
{
return 'B.res';
}
function render()
{
parent::render();
echo 'Rendering B class : ' . $this->service->get('title');
}
}
class Proxy
{
private $mSite = null;
public function __construct($site)
{
$this->mSite = $site;
}
public function get($key)
{
// problem here
}
}
// in the main script
$obj = new B();
$obj->render();
Question is: in method 'get' of class 'Proxy', how I extract the corresponding resource file name (resourceFile returns the name) by using only $mSite (object pointer)?
What about:
public function get($key)
{
$file = $this->mSite->resourceFile();
}
But this requires A::resourceFile() to be public otherwise you cannot access the method from outside the object scope - that's what access modifiers have been designed for.
EDIT:
OK - now I think I do understand, what you want to achieve. The following example should demonstrate the desired behavior:
class A
{
private function _method()
{
return 'A';
}
public function render()
{
echo $this->_method();
}
}
class B extends A
{
private function _method()
{
return 'B';
}
public function render()
{
parent::render();
echo $this->_method();
}
}
$b = new B();
$b->render(); // outputs AB
But if you ask me - I think you should think about your design as the solution seems somewhat hacky and hard to understand for someone looking at the code.

Categories