is there a class to handle $_REQUEST that makes the life of a php developer easier?
I want to handle the client requests easier.
I dont want to test with if(is_set($_REQUEST['blabla'])) {makesomthing();}
I wish there could be a solution like this.
class rpclike
{
public function getMember()
{
$memberid = $this->inputhandler['memberid'];
$member = $this->memberclass->getmember($memberid);
foreach($member as $mem)
{
echo $mem->id;
}
}
}
$rpc = new rpclike();
then if i call the rpclike from a javascript like this
Get member
Which class can do something like that?
It's not recommended that you use $_REQUEST as it poses security concerns. You should be using one of $_GET, $_POST, or $_COOKIE depending on what global request var you are trying to retrieve. Your best bet would be to have something like the following:
class input {
public static function get($key, $value = false) {
return (!empty($_GET[$key])) ? $_GET[$key] : $value;
}
public static function post($key, $value = false) {
return (!empty($_POST[$key])) ? $_POST[$key] : $value;
}
public static function cookie($key, $value = false) {
return (!empty($_COOKIE[$key])) ? $_COOKIE[$key] : $value;
}
}
You could then use the class like:
if (input::post('field', null) != null) {
}
or
if (input::get('field', false) != false) {
}
Although this still requires testing, you can explicitly set the return values in the event no data was set for the global variable.
PHP doesn't really have a default class structure that you can utilize in that kind of manner, as it's origins are in procedural-based programming.
It would be fairly trivial for you to create a class like that if you felt the need for it. However, you would really just be adding overhead. If the convenience of it is worth it for you, then you could utilize the __get() and __set() methods to handle existence checks for you.
The fact that you want to use this for handling client requests in an easier fashion is probably a good indicator that you should move to something like an MVC framework, which usually handle URLs and route them to appropriate methods for you automatically. Most PHP frameworks will do this for you already. For a nice overview on how the process commonly works, you could see how CodeIgniter does it.
Aside from the obvious security risks involved in this, it is feasible. It's a common pattern to use for steering requests in an MVC system.
Say you request index.php?class=User&method=ViewProfile
$module = new $_GET['class']();
if(!method_exists($module,$_GET['method']))
$module->$eventName();
I don't think so. Being able to invoke an arbitrary method would be a massive security hole.
Do something like:
url: /foo/bar?req=getMembers&memberid=22
Then you can do:
$request = $_GET['req'];
$request();
Slightly less dangerous version:
$req_methods = array(
getMembers => 'some_function',
saveMembers => 'another_function',
sendMessage => 'send_him_an_email'
);
$request = $_GET['req'];
$req_methods[$request]();
Related
I'm adding a funcionality to this custom Router and custom Request Class in order to be able to serve pages and json responses.
I'm stuck on the router part, where the Route has a parameter in the url like:
example.com/apply/{variable}
Those are the classes:
Router class:
<?php
class Router
{
private $request;
private $supportedHttpMethods = array("GET", "POST");
function __construct(RequestInterface $request)
{
$this->request = $request;
}
function __call($name, $args)
{
list($route, $method) = $args;
if (!in_array(strtoupper($name), $this->supportedHttpMethods)) {
$this->invalidMethodHandler();
}
$this->{strtolower($name)}[$this->formatRoute($route)] = $method;
}
/**
* Removes trailing forward slashes from the right of the route.
*
* #param route (string)
*/
private function formatRoute($route)
{
$result = rtrim($route, '/');
if ($result === '') {
return '/';
}
return $result;
}
private function invalidMethodHandler()
{
header("{$this->request->serverProtocol} 405 Method Not Allowed");
}
private function defaultRequestHandler()
{
header("{$this->request->serverProtocol} 404 Not Found");
}
/**
* Resolves a route
*/
function resolve()
{
$methodDictionary = $this->{strtolower($this->request->requestMethod)};
$formatedRoute = $this->formatRoute($this->request->requestUri);
$method = $methodDictionary[$formatedRoute];
if (is_null($method)) {
$this->defaultRequestHandler();
return;
}
echo call_user_func_array($method, array(
$this->request
));
}
function __destruct()
{
$this->resolve();
}
}
Request Class:
<?php
include_once 'RequestInterface.php';
class Request implements RequestInterface
{
private $params = [];
public function __construct()
{
$this->bootstrapSelf();
}
private function bootstrapSelf()
{
foreach ($_SERVER as $key => $value) {
$this->{$this->toCamelCase($key)} = $value;
}
}
private function toCamelCase($string)
{
$result = strtolower($string);
preg_match_all('/_[a-z]/', $result, $matches);
foreach ($matches[0] as $match) {
$c = str_replace('_', '', strtoupper($match));
$result = str_replace($match, $c, $result);
}
return $result;
}
public function isPost()
{
return $this->requestMethod === "POST";
}
/**
* Implemented method
*/
public function getParams()
{
if ($this->requestMethod === "GET") {
$params = [];
foreach ($_GET as $key => $value) {
$params[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
}
$this->params = array_merge($this->params, $params);
}
if ($this->requestMethod == "POST") {
$params = [];
foreach ($_POST as $key => $value) {
$params[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
}
$this->params = array_merge($this->params, $params);
}
return $this->params;
}
}
This is how I would call the Router:
$router->get('/apply/{code}', function($request) use($myClass) {});
Which approach would be the better? I don't know how to resolve that.
I would strongly recommend having a look into existing http factory implementations before reinventing the wheel from scratch. Even custom implementations may look like they provide some flexibility and benefits in short term, you can easily shoot your own foot in mid/long term by building an application based on such approach.
Both the language itself and PHP ecosystem is evolved a lot, we are an in 2019, we have dozens of well-written, re-usable libraries around. Just pick your weapons and focus on your real goal. Any code without tests, involving magic, lacks from composer, a proper autoloading mechanism, a well-written router or a fast template engine; most of the time will cause more pain than the value it provides. We should stop repeating ourselves.
As far as I understand, your goal is serving JSON content on a specific URI path but you are trying to invent a Router. If your goal is writing a proper router, there is nothing to do with Request/Response interfaces which mentioned in question. I would recommend to have a look implementations of some reusable, framework-independent routers such as FastRoute, Zend Router, Aura Router etc to have an idea first. Implementing a proper router is of course not rocket science but its not simple as you may realized. Still, trying to write that component can be educational as well and if your goal is this, go for it.
Here is a couple of tips (and new problems to think about):
There is a PSR-15 Request Handler standard. Sending headers in private methods named requestHandler may not be a good idea.
Request handlers and routers are different components and requires different workflows. You are mixing them in your code and this is a warning sign.
Any code involving __magic is setting up a trap for yourself and for the potential future developers.
I am not sure the result of include_once 'RequestInterface' line but we have HTTP Message interfaces. I would consider having a use Psr\Http\Message\ServerRequestInterface import in any type of custom implementation when dealing with requests.
Echoing in __destruct is also interesting. What you need here is an emitter. A few examples: Http Emitter, Zend Http Runner
And here is a high level answer for your actual question: you need to implement a mechanism (probably using regex) to catch patterns in URI parts and parse and detect optional or required named parts in "paths".
Personally, I would recommend to have a look into Zend Expressive. It helps developers a lot when writing lightweight, middleware-driven applications. Best feature of Expressive is you can pick any weapon according to your needs. It is not a full-blown MVC framework, provides a new way to write web applications and it's damn fast. You can freely choose any component you want, for example; Twig for rendering needs, Symfony Console for CLI, Zend Service Manager as dependency injection container, Aura Router for routing etc..
You can give a try it using only a few commands (assuming you have globally installed composer):
composer create-project zendframework/zend-expressive-skeleton my-app
cd my-app
composer run --timeout=0 serve
And open your browser: http://localhost:8080
Good luck!
example:
$CONF = parse_ini_file('cfg.ini', true);
$EmailUN = $CONF['EM']['key01'];
$EmailPW = $CONF['EM']['key02'];
$EmailTO = $CONF['EM']['key03'];
$SMSAPI = $CONF['SMS']['key01'];
$SMSUN = $CONF['SMS']['key02'];
$SMSPW = $CONF['SMS']['key03'];
$SMSNUM = $CONF['SMS']['key04'];
is there a more effective/elegant way to import this data? i want to learn best practices while im still learning. i dont want to fill up the whole top of my php doc with objects calling for keys. if this is a duplicate i apologize in advance.
One approach would be a helper function that accesses your configuration.
If for example you wanted to access your configuration like this:
config('SMS.key01');
You could define the config function as:
function config(string $key)
{
static $config = parse_ini_file('cfg.ini', true);
$curr = $config;
foreach(explode('.', $key) as $segment) {
if(!isset($curr[$segment]) {
return null;
} else if(is_array($curr[$segment])) {
$curr = $curr[$segment];
continue;
}
return $curr[$segment];
}
}
The above is a minimal example roughly based on Laravel's config and array_get helpers. Your helper function would likely need to be implemented differently in the context of your application or framework.
The main advantage of this approach is that all of the implementation details of how your configuration is stored and accessed is contained within the function. What you are doing right now bleeds low level implementation details into the rest of your code.
I need to know if there is a better way to avoid Call to a member function xxxx() on null
currently I'm coding as follows but it is cumbersome.
if($event->getForm()
&& $event->getForm()->getParent()
&& $event->getForm()->getParent()->getParent()
&& $event->getForm()->getParent()->getParent()->getData()
&& $event->getForm()->getParent()->getParent()->getData()->getComponente()
){
$componente = $event->getForm()->getParent()->getParent()->getData()->getComponente();
$formModifier($event->getForm(), $componente, $defaultComponente);
}
In PHP 7 this is actually a catchable Error (if you're using hhvm it's a regular Exception):
try {
$componente = $event->getForm()->getParent()->getParent()->getData()->getComponente();
} catch (\Error $e) {
$componente = null;
}
if ($componente !== null) {
$formModifier($event->getForm(), $componente, $defaultComponente);
}
In PHP 5 there is a workaround using intermediate variables and the and keyword instead of &&:
if (
$f = $event->getForm() and
$p = $f->getParent() and
$p2 = $p->getParent() and
$d = $p2->getData() and
$componente = $d->getComponente()
) {
$formModifier($f, $componente, $defaultComponente);
}
If you use && instead of and you'll get "undefined variable" notices and this workaround won't work.
Working examples: https://3v4l.org/0S6ps
no there is no way, but at least you can do some performance improvement
$form = $event->getForm();
if(!$form){
//do error handling
return;
}
$parent = $form->getParent();
if(!$parent){
//do error handling
return;
}
$p_parent = $parent->getParent();
if(!$p_parent){
//do error handling
return;
}
$data = $p_parent->getData();
if(!$data){
//do error handling
return;
}
$component = $data->getComponente();
...
this way you call each function only once and you can do better error handling
I think this is a great example of a bad code. By having a code like this you're breaking several rules and making your life much harder than it should be.
Your code is rigid, fragile, hard to understand and maintain etc.
Simpler is ALWAYS better.
If you can't make your $xx->getComponent() a proper object easily accessible without such ugly nested relationship, you should at least encapsulate the method into something appropriate and use that instead, so if anything changes, you don't have to go full mental and change it all over the place.
This class seems strange in it's creation, but if you are not extracting these methods dynamically using __call(), you can use method_exists() in a loop inside a function, something similar to:
function getMethodChain($class,$arr = ['getForm','getParent','getParent','getData','getComponente'])
{
# First check the object is set
if(!is_object($class))
return false;
# Loop intended method chain
foreach($arr as $method) {
# Check if the method exists in the current class or passed already
$useClass = (!isset($classPass))? $class : $classPass;
# Check if the method exists in the current class
if(is_object($useClass) && method_exists($useClass,$method)) {
# Assign this class/method to use next in the loop
$classPass = $useClass->{$method}();
}
else
return false;
}
# Just send back
return (isset($classPass))? $classPass : false;
}
The use would be something like:
# This will either be the data you expect or false
$componente = getMethodChain($event);
I'm experimenting with php mvc and I'm stucked with the following issue. My request and router classes are really simple and I would like to extend theme to can handle controller calls from sub folders and to controller classes functions should be able to pick up url variables send it threw get and post.
my router looks as it follows
class Router{
public static function route(Request $request){
$controller = $request->getController().'Controller';
$method = $request->getMethod();
$args = $request->getArgs();
$controllerFile = __SITE_PATH.'/controllers/'.$controller.'.php';
if(is_readable($controllerFile)){
require_once $controllerFile;
$controller = new $controller;
if(!empty($args)){
call_user_func_array(array($controller,$method),$args);
}else{
call_user_func(array($controller,$method));
}
return;
}
throw new Exception('404 - '.$request->getController().'--Controller not found');
}
}
and Request class
private $_controller;
private $_method;
private $_args;
public function __construct(){
$parts = explode('/',$_SERVER['REQUEST_URI']);
$this->_controller = ($c = array_shift($parts))? $c: 'index';
$this->_method = ($c = array_shift($parts))? $c: 'index';
$this->_args = (isset($parts[0])) ? $parts : array();
}
public function getController(){
return $this->_controller;
}
public function getMethod(){
return $this->_method;
}
public function getArgs(){
return $this->_args;
}
}
The problem is:when I try to send threw ajax, variables to a controller method this are not recognized because of its url structure.
For example
index/ajax?mod_title=shop+marks&domain=example
is accepted just if it look
index/ajax/shop+mark/example
Your code contains what is known as an LFI vulnerability and is dangerous in its current state.
You should whitelist your what can be used as your $controller, as otherwise an attacker could try to specify something using NUL bytes and possibly going up a directory to include files that SHOULD NOT be ever included, such as /etc/passwd, a config file, whatever.
Your router is not safe for use; beware!
edit: example on whitelisting
$safe = array(
'ajax',
'somecontroller',
'foo',
'bar',
);
if(!in_array($this->_controller, $safe))
{
throw new Exception(); // replace me with your own error 404 stuff
}
Since your Request class uses a URI segments approach for identifying controller, action and arguments, global variables such as $_GET or $_REQUEST are not taken into account from within your Request.
What you need to do is to make some additions to your Request code. Specifically:
Remove the line:
$this->_args = (isset($parts[0])) ? $parts : array();
And add the following:
$all_parts = (isset($parts[0])) ? $parts : array();
$all_parts['get'] = $_GET;
$this->_args = $all_parts;
This way, $_GET (ie variables passed via the url) variables will be available in the actions called, as they will be in $args (they will be available as $args['get'] actually, which is the array that holds the $_GET vars, so you will be able to have access to domain=example by using $args['get']['domain']).
Ofcourse, you can add one more method in your Request class (e.g. query) that might look like that:
public function query($var = null)
{
if ($var === null)
{
return $_GET;
}
if ( ! isset($_GET[$var]) )
{
return FALSE;
}
return $_GET[$var];
}
This way, you can get a single variable from the url (e.g. $request->query('domain')) or the whole $_GET array ($request->query()).
That's because php will put "?mod_title=..." in the $_GET array automatically. Your getArgs() function should check for $_GET, $_POST or $_REQUEST.
If you're trying for a minimal MVC approach, have a look at rasmus' example: http://toys.lerdorf.com/archives/38-The-no-framework-PHP-MVC-framework.html
If your use case is going to get more complex, have a look at how Zend (http://framework.zend.com/manual/en/zend.controller.html) or Symfony (https://github.com/symfony/symfony/tree/master/src/Symfony/Component/Routing) do their stuff.
Choose any popular MVC to see how they implement it under the hood. In addition, spl_autoload_register and namespace are your friends.
In PHP I would like to be able to access PUT and DELETE vars globally similar to how GET and POST vars are accessed globally. I originally considered adding the data to $_PUT and $_DELETE respectively in the global namespace, but then I realized that the data for each request is stored in the message body so there's no way for there to be more than one dataset from a POST, PUT, or DELETE request.
Are there any side-effects of overwriting the $_POST variable?
i.e. str_parse( file_get_contents( 'php://input' ), $_POST );
Am I being silly, or is there a better way to access PUT and DELETE data?
Edit to clarify my thinking:
I am very well aware of the source of the data in $_POST, in fact i mentioned it earlier in my question. If a HTTP POST request is sent to the server the data is stored in php://input. If a HTTP PUT or DELETE request is sent to the server, the data is stored in the exact same place, meaning that $_POST will be empty (as no data was POSTed despite data being available.
A GET request, on the other hand, is passed via the query string. This allows simultaneous passing of $_POST and $_GET variables. It is not possible to simultaneously pass POST and PUT or DELETE variables.
If I overwrite $_POST from php://input on PUT and or DELETE requests, there is no data loss.
The alternative of adding:
global $_PUT;
global $_DELETE;
to the beginning of functions seems silly, as I'll only be able to use one at a time anyway.
My first question, which is the one I really want answered, is about what side-effects or issues exist in overwriting $_POST. I can't possibly be the first person to try something as silly as:
$_POST['foo'] = 'bar';
I'm just concerned that if I do anything similar that it might not be preserved across scopes.
You'll see this called "bad practice" all over the internet, but if you really get in to why it is "bad practice", well, the answers get fuzzy. The most concrete reason is the "hit by a bus" scenario so often bandied about - what if the project gets handed off to a new developer?
Hand wringing aside (you can leave comments, after all), there really isn't a compelling reason not to do it like this, but again, there isn't a compelling reason to do it, either. Why not put the values in a $_SESSION key if you want them global? Or make a global variable? Or make a static class to access the PUT/DELETE values through? With all the other optional approaches, I think that overwriting $_POST, while it won't make your server explode, is the most likely to cause you a headache down the road.
I threw this little static class together, you'll want to test this out before relying on it. Use:
//To check if this is a rest request:
Rest::isRest();
//To get a parameter from PUT
$put_var = Rest::put('keyname', false);
//To get a parameter from DELETE
$dele_var = Rest::delete('keyname', false);
class Rest {
static private $put = false;
static private $delete = false;
static private $is_rest = false;
function __construct() {
self::$is_rest = true;
switch ($_SERVER['REQUEST_METHOD']) {
case 'PUT':
parse_str(self::getVars(), self::$put);
break;
case 'DELETE':
parse_str(self::getVars(), self::$delete);
break;
default:
self::$is_rest = false;
}
}
private static function getVars() {
if (strlen(trim($vars = file_get_contents('php://input'))) === 0)
$vars = false;
return $vars;
}
public static function delete($key=false, $default=false) {
if (self::$is_rest !== true)
return $default;
if (is_array(self::$delete) && array_key_exists($key, self::$delete))
return self::$delete[$key];
return $default;
}
public static function put($key=false, $default=false) {
if (self::$is_rest !== true)
return $default;
if (is_array(self::$put) && array_key_exists($key, self::$put))
return self::$put[$key];
return $default;
}
public static function isRest() {
return self::$is_rest;
}
}
Leave Post and Get as it is. it shouldn't be modified as it's for reading purposes only. Create $_PUT and $_DELETE globals:
// globals
$_DELETE = array ();
$_PUT = array ();
switch ( $_SERVER['REQUEST_METHOD'] ) {
case !strcasecmp($_SERVER['REQUEST_METHOD'],'DELETE'):
parse_str( file_get_contents( 'php://input' ), $_DELETE );
break;
case !strcasecmp($_SERVER['REQUEST_METHOD'],'PUT'):
parse_str( file_get_contents( 'php://input' ), $_PUT );
break;
}
Not tested but you should get the idea. I was in the search for a rest framework myself some weeks ago and decided to go with python. Recess (http://www.recessframework.org/) sounds promising though
You shouldn't modify $_POST directly as this represents values coming from the client. Consider it read-only, and do any modifications in user-defined variable.
As a follow up regarding accessing PUT and DELETE data, currently there is no superglobal built in to PHP to access this data directly. As the data is file data, which can be rather large, the usefulness and efficiency of reading the entire file contents in a typical assignment statement $variable = $_PUT['file']; is questionable. Instead, it should be read in chunks. As such, the usage is consistent with reading from any other file input resource.
More on PUT here:
http://php.net/manual/en/features.file-upload.put-method.php
If you create a "request" object, then regardless whether the request comes over HTTP, command line, or through an HTML5 web socket, you will have a uniform way to access request data. You can then make the request object accessible in the global scope, or pass it as an argument to the required functions or methods.
Ideally you would store data that is independent of the request in static or global variables, e.g. settings that are "static" regardless of the request, and data specific to the request in a local variable or object, that could be used by your business logic. If you had a web socket server, for example, it would be easier to handle multiple request objects in a single PHP process. Here is an example that might help:
$headers = getallheaders();
$query = parse_str($_SERVER['QUERY_STRING']);
$data = file_get_contents('php://input');
if(strpos($headers['Content-Type'],'application/x-www-form-urlencoded') !== false)
{
$data = parse_str($data);
}
elseif(strpos($headers['Content-Type'],'application/json') !== false)
{
$data = json_decode($data);
}
elseif(strpos($headers['Content-Type'],'application/soap+xml') !== false)
{
$data = // parse soap
}
elseif(strpos($headers['Content-Type'],'application/xml') !== false)
{
$data = // parse xml
}
// else ...
$request = new Request($_SERVER['REQUEST_METHOD'],$data,$query);
// example business logic
$method = $request->get_request_method();
$obj = new BlogPost();
if($method == 'GET')
{
$obj->id($request->get_query('id'));
$obj->load();
}
elseif($method == 'PUT')
{
$obj->id($request->get_query('id'));
$obj->title($request->get_data('title'));
$obj->body($request->get_data('body'));
$obj->save();
}
elseif($method == 'POST')
{
$obj->title($request->get_data('title'));
$obj->body($request->get_data('body'));
$obj->save();
}
elseif($method == 'DELETE')
{
$obj->id($request->get_query('id'));
$obj->wipe();
}
Regardless of whether it is a PUT, POST, PATCH, or DELETE, there is only one body of data in the HTTP request, so your application does not need a complex $request object. The request object can make your controller (if you are using MVC) very simple.