I'm trying to find some simple examples of a PHP class accessing a distributed API.
Whenever I search for a PHP API client it gives way too much information on building the API itself and little in the way of real access methods beyond the curl examples. A PHP class that includes a good curl method and a way to handle the requests would be nice. I learn by example and lots of re-arranging.
I started with a construct to catch the usual connection settings:
private $hosts = array( URI_1, URI_2, URI_3);
private $users = array( USER_1, USER_2, USER_3);
private $pass = array( PASS_1, PASS_2, PASS_3);
public function __construct($request = array())
{
if (is_array($request)) {
if(isset($request['hostname'])) {
if(in_array($request['hostname'], $hosts)) {
$this->input['hostname'] = $request['hostname'];
}
}
if (isset($request['username'])) {
if(in_array($request['username'], $users)) {
$this->input['username'] = $request['username'];
}
}
if (isset($request['password'])) {
if (in_array($request['password'], $pass)) {
$this->input['password'] = $request['password'];
}
}
if (isset($this->input['hostname']) &&
isset($this->input['username']) &&
isset($this->input['password'])) {
return true;
}
} else {
return false;
}
}
Some simple examples of best practices would make this a lot easier.
Try out Guzzle:
Guzzle takes the pain out of sending HTTP requests and the redundancy
out of creating web service clients.
Guzzle is a framework that includes the tools needed to create a
robust web service client, including: Service descriptions for
defining the inputs and outputs of an API, resource iterators for
traversing paginated resources, batching for sending a large number of
requests as efficiently as possible.
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!
Hello StackOverflow community
Currently I'm in small project, where a API is required.
I tested some SOAP WSDL and restFul API's, but none worked for me. So I created my own API. As I'm not a professional programer, I want to know if my API is unsafe.
<?php
class API{
public function __construct(){
if($_SERVER['REQUEST_METHOD'] === 'POST'){
if(!empty($_POST)){
$data_array = array();
require_once('PreparePOST.php');
foreach($_POST as $key => $value)
array_push($data_array,array($value => $key));
echo new PreparePOST($data_array);
}else{return new Exception('No Data Requested');}
}else{return new Exception('Request not allowed');}
}
}$init = new API();
?>
Some serious validation and security stuff is done after PreparePOST($data_array);
E.g. only allowed parameters and character escaping.
Notice: This is only the POST implementation the GET implementation and an API Auth Token will be available later.
What are you thinking?
Is this compete nonsense?
Where are possible security issues?
How can I improove my code?
Btw. my project is a Tool, which transmits server infos from our customers (like HD capacity and backup logs) to our server, so we evaluate all server statistically.
Thanks for you advice
KR
As you say that the PreparePOST object does validation and security it would be ideal to share that code too to identify security issues.
One thing which springs to mind right away although not a security issue is that you should throw an Exception rather than return one. Also when you are initializing your API object you don't have any way to catch the exceptions which it may throw. See the try catch block which I've put below.
Also the code isn't super readable, perhaps this would be better:
<?php
class API
{
public function __construct()
{
if($_SERVER['REQUEST_METHOD'] === 'POST')
{
if(!empty($_POST))
{
$data = array();
require_once('PreparePOST.php');
foreach($_POST as $key => $value)
{
array_push($data, array($value => $key));
}
echo new PreparePOST($data);
}
else
{
throw new Exception('No Data Requested');
}
}
else
{
throw new Exception('Request not allowed');
}
}
}
try
{
$init = new API();
}
catch(Exception $e)
{
//handle the exception here
echo $e->getMessage();
}
?>
Specific coding style is down to preference however to keep it readable and maintainable you should definitely make good use of whitespace. There's absolutely nothing gained by keeping code bunched together. In fact quite the opposite.
I wouldn't say there's a need to call your $data variable $data_array. You can see from the code that it's an array, also separate arguments to functions with a comma and a space for readability ,. (When calling array_push).
my web app requires making 7 different soap wsdl api requests to complete one task (I need the users to wait for the result of all the requests). The avg response time is 500 ms to 1.7 second for each request. I need to run all these request in parallel to speed up the process.
What's the best way to do that:
pthreads or
Gearman workers
fork process
curl multi (i have to build the xml soap body)
Well the first thing to say is, it's never really a good idea to create threads in direct response to a web request, think about how far that will actually scale.
If you create 7 threads for everyone that comes along and 100 people turn up, you'll be asking your hardware to execute 700 threads concurrently, which is quite a lot to ask of anything really...
However, scalability is not something I can usefully help you with, so I'll just answer the question.
<?php
/* the first service I could find that worked without authorization */
define("WSDL", "http://www.webservicex.net/uklocation.asmx?WSDL");
class CountyData {
/* this works around simplexmlelements being unsafe (and shit) */
public function __construct(SimpleXMLElement $element) {
$this->town = (string)$element->Town;
$this->code = (string)$element->PostCode;
}
public function run(){}
protected $town;
protected $code;
}
class GetCountyData extends Thread {
public function __construct($county) {
$this->county = $county;
}
public function run() {
$soap = new SoapClient(WSDL);
$result = $soap->getUkLocationByCounty(array(
"County" => $this->county
));
foreach (simplexml_load_string(
$result->GetUKLocationByCountyResult) as $element) {
$this[] = new CountyData($element);
}
}
protected $county;
}
$threads = [];
$thread = 0;
$threaded = true; # change to false to test without threading
$counties = [ # will create as many threads as there are counties
"Buckinghamshire",
"Berkshire",
"Yorkshire",
"London",
"Kent",
"Sussex",
"Essex"
];
while ($thread < count($counties)) {
$threads[$thread] =
new GetCountyData($counties[$thread]);
if ($threaded) {
$threads[$thread]->start();
} else $threads[$thread]->run();
$thread++;
}
if ($threaded)
foreach ($threads as $thread)
$thread->join();
foreach ($threads as $county => $data) {
printf(
"Data for %s %d\n", $counties[$county], count($data));
}
?>
Note that, the SoapClient instance is not, and can not be shared, this may well slow you down, you might want to enable caching of wsdl's ...
I need to perform some fairly heavy queries with Amazon's AWS SDK for PHP.
The most efficient way would be to use PHP's MultiCurl. It seems that Guzzle already has functionality for MultiCurl built in.
Does using the standard methods provided by the AWS SDK automatically use MultiCurl or do I have to specify it's usage directly? E.g. calling $sns->Publish() 30 times.
Thanks!
Parallel requests work exactly the same in the SDK as in plain Guzzle and do take advantage of MultiCurl. For example, you could do something like this:
$message = 'Hello, world!';
$publishCommands = array();
foreach ($topicArns as $topicArn) {
$publishCommands[] = $sns->getCommand('Publish', array(
'TopicArn' => $topicArn,
'Message' => $message,
));
}
try {
$successfulCommands = $sns->execute($publishCommands);
$failedCommands = array();
} catch (\Guzzle\Service\Exception\CommandTransferException $e) {
$successfulCommands = $e->getSuccessfulCommands();
$failedCommands = $e->getFailedCommands();
}
foreach ($failedCommands as $failedCommand) { /* Handle any errors */ }
$messageIds = array();
foreach ($successfulCommands as $successfulCommand) {
$messageIds[] = $successfulCommand->getResult()->get('MessageId');
}
// Also Licensed under version 2.0 of the Apache License.
The AWS SDK for PHP User Guide has more information about working with command objects in this way.
I've built a first-run web service on Zend Framework (1.10), and now I'm looking at ways to refactor some of the logic in my Action Controllers so that it will be easier for me and the rest of my team to expand and maintain the service.
I can see where there are opportunities for refactoring, but I'm not clear on the best strategies on how. The best documentation and tutorials on controllers only talk about small scale applications, and don't really discuss how to abstract the more repetitive code that creeps into larger scales.
The basic structure for our action controllers are:
Extract XML message from the request body - This includes validation against an action-specific relaxNG schema
Prepare the XML response
Validate the data in the request message (invalid data throws an exception - a message is added to the response which is sent immediately)
Perform database action (select/insert/update/delete)
Return success or failure of action, with required information
A simple example is this action which returns a list of vendors based on a flexible set of criteria:
class Api_VendorController extends Lib_Controller_Action
{
public function getDetailsAction()
{
try {
$request = new Lib_XML_Request('1.0');
$request->load($this->getRequest()->getRawBody(), dirname(__FILE__) . '/../resources/xml/relaxng/vendor/getDetails.xml');
} catch (Lib_XML_Request_Exception $e) {
// Log exception, if logger available
if ($log = $this->getLog()) {
$log->warn('API/Vendor/getDetails: Error validating incoming request message', $e);
}
// Elevate as general error
throw new Zend_Controller_Action_Exception($e->getMessage(), 400);
}
$response = new Lib_XML_Response('API/vendor/getDetails');
try {
$criteria = array();
$fields = $request->getElementsByTagName('field');
for ($i = 0; $i < $fields->length; $i++) {
$name = trim($fields->item($i)->attributes->getNamedItem('name')->nodeValue);
if (!isset($criteria[$name])) {
$criteria[$name] = array();
}
$criteria[$name][] = trim($fields->item($i)->childNodes->item(0)->nodeValue);
}
$vendors = $this->_mappers['vendor']->find($criteria);
if (count($vendors) < 1) {
throw new Api_VendorController_Exception('Could not find any vendors matching your criteria');
}
$response->append('success');
foreach ($vendors as $vendor) {
$v = $vendor->toArray();
$response->append('vendor', $v);
}
} catch (Api_VendorController_Exception $e) {
// Send failure message
$error = $response->append('error');
$response->appendChild($error, 'message', $e->getMessage());
// Log exception, if logger available
if ($log = $this->getLog()) {
$log->warn('API/Account/GetDetails: ' . $e->getMessage(), $e);
}
}
echo $response->save();
}
}
So - knowing where the commonalities are in my controllers, what's the best strategy for refactoring while keeping it Zend-like and also testable with PHPUnit?
I did think about abstracting more of the controller logic into a parent class (Lib_Controller_Action), but this makes unit testing more complicated in a way that seems to me to be wrong.
Two ideas (just creating an answer from the comments above):
Push commonality down into service/repository classes? Such classes would be testable, would be usable across controllers, and could make controller code more compact.
Gather commonality into action helpers.
Since you have to do this step every time a request is made, you could store that receive, parse and validate the received request in a Zend_Controller_Plugin which would be run every PreDispatch of all controllers. (Only do-able if your XML request are standardized) (If you use XMLRPC, REST or some standard way to build requests to your service, you could look forward those modules built in ZF)
The validation of the data (action specific) could be done in a controller method (which would then be called by the action(s) needing it) (if your parametters are specific to one or many actions of that controller) or you could do it with the patterns Factory and Builder in the case that you have a lot of shared params between controllers/actions
// call to the factory
$filteredRequest = My_Param_Factory::factory($controller, $action, $paramsArray) // call the right builder based on the action/controller combination
// the actual Factory
class My_Param_Factory
{
public static function factory($controller, $action, $params)
{
$builderClass = "My_Param_Builder_" . ucfirst($controller) . '_' . ucfirst($action);
$builder = new $builderClass($params);
return $builder->build();
}
}
Your builder would then call specific parameters validating classes based on that specific builder needs (which would improve re-usability)
In your controller, if every required params are valid, you pass the processing to the right method of the right model
$userModel->getUserInfo($id) // for example
Which would remove all of the dataprocessing operations from the controllers which would only have to check if input is ok and then dispatch accordingly.
Store the results (error or succes) in a variable that will be sent to the view
Process the data (format and escape (replace < with < if they are to be included in the response for example)), send to a view helper to build XML then print (echo) the data in the view (which will be the response for your user).
public function getDetailsAction()
{
if ($areRequestParamsValid === true) {
// process data
} else {
// Build specific error message (or call action helper or controller method if error is general)
}
$this->view->data = $result
}