tebru / retrofit-php Goto Github PK
View Code? Open in Web Editor NEWRetrofit implementation in PHP. A REST client for PHP.
License: Other
Retrofit implementation in PHP. A REST client for PHP.
License: Other
Hi,
I'm trying php 7.4 and I got a deprecation warning about the line below
According to php upgrading file ReflectionNamedType::getName()
should be used instead
https://github.com/php/php-src/blob/PHP-7.4.0/UPGRADING#L417
Cookie
Cookies
CookieMap
PartMap
Field
FieldMap
HeaderMap
Sometimes there's a race condition where the generated file will attempt to be created twice and will result in a can't redeclare class exception.
The @Body
annotation should have an option jsonSerializable
, which will json encode the object before attempting to serialize it.
Since the package is relying on specific versions of some components, (symfony/console, symfony/filesystem) it's not working on symfony 2.7
Add comment about method definitions required to be on one line.
This flies directly in the face of #22, but it'd be great to see the composer dependency for jms/serializer updated to ~1.0. Not sure what implications that has on the library, but suspecting that's a quicker thing to investigate than adding in all the adapters for serializer behavior that the other ticket will require.
Create adapters for jms/symfony serializers
I found that retrofit-php supports asynchronous calls but as callbacks it requires an instance of \Tebru\Retrofit\Http\Callback Such a callback is OK in Java since it supports lambdas only starting from 8 version but it supports anonymous classes. In php though it's not OK because php supports lambdas very long time but it supports anonymous classes only starting from php7. So I need to create a class which implements Callback interface somewhere else then create an instance of this class and only after that pass it to my client function. It's very inconvenient. So I wonder if it possible to use success/failure lambdas to handle response asynchronously?
Through fortuitous type changing when serializing null, the body gets passed to Guzzle as false
instead of null
and guzzle only does a falsey check for the body. It would be better to do a check if body is null and not attempt serialization at all.
It would be useful to set headers using logic instead of just being a simple string. One use case of this is to set X-Forwarded-For to the end user's IP.
Allow cache to create multiple client file that can be autoloaded instead of requiring one cache file.
Add @Field
for use instead
Hello, I'm trying to use the ResponseBody
annotation to transform a response that returns an array of objects. Does the annotation support array syntax? I was expecting to use something like…
@ResponseBody("MyNamespace/MyClass[]")
…but I'm getting a ResponseHandlingFailedException: "Retrofit: Could not convert response body"
exception.
If there are no registered services, when the Cache Writer calls clean() it generates an empty file with just "<?php". PHP will not parse this as valid PHP unless there is a new line after the opening PHP tag.
https://github.com/tebru/retrofit-php/blob/master/src/Cache/CacheWriter.php#L99
Need to change '<?php' to "<?php\n".
Rather than using a HandlerFactory
with specific methods for creating each individual handler type, have you considered either just creating the handlers within the DynamoMethodListener
directly or maybe modifying the HandlerFactory
to return an array of handlers? Right now the HandlerFactory
doesn't provide a ton of value (aside from potentially mock-ability within tests) and is IMO too aware of all of the possible implementations of handlers.
Current Implementation
$handlers = [
$this->handlerFactory->baseUrl($methodModel, $methodBodyBuilder, $annotations),
$this->handlerFactory->serializationContext($methodModel, $methodBodyBuilder, $annotations),
$this->handlerFactory->requestUrl($methodModel, $methodBodyBuilder, $annotations),
$this->handlerFactory->requestHeader($methodModel, $methodBodyBuilder, $annotations),
$this->handlerFactory->requestBody($methodModel, $methodBodyBuilder, $annotations),
$this->handlerFactory->returns($methodModel, $methodBodyBuilder, $annotations),
$this->handlerFactory->asyncCallback($methodModel, $methodBodyBuilder, $annotations),
];
Option A
Create the handlers inline. This would remove the necessity of the factory entirely.
$handlers = [
new BaseUrlHandler($methodModel, $methodBodyBuilder, $annotations),
new SerializationContextHandler($methodModel, $methodBodyBuilder, $annotations),
new RequestUrlHandler($methodModel, $methodBodyBuilder, $annotations),
new RequestHeaderHandler($methodModel, $methodBodyBuilder, $annotations),
new RequestBodyHandler($methodModel, $methodBodyBuilder, $annotations),
new ReturnHandler($methodModel, $methodBodyBuilder, $annotations),
new AsyncHandler($methodModel, $methodBodyBuilder, $annotations),
];
Option B
Make the handler factory responsible for creating the handlers. With this approach, the listener doesn't know or care what type of handlers are handling the class.
$handlers = $this->handlerFactory->create($methodModel, $methodBodyBuilder, $annotations);
Option C
Allow for stateless handlers. This would be quite a bit more of a refactor, but you could change the abstract Handler
into a HandlerInterface
...
<?php
namespace Tebru\Retrofit\Generation\Handler;
interface HandlerInterface
{
/**
* @param MethodModel $methodModel
* @param MethodBodyBuilder $methodBodyBuilder
* @param AnnotationCollection $annotations
*/
public function handle(MethodModel $methodModel, MethodBodyBuilder $methodBodyBuilder, AnnotationCollection $annotations);
}
Then you could inject an array of HandlerInterface
's into your listener and delegate accordingly.
<?php
namespace Tebru\Retrofit\Generation\Listener;
use Tebru\Dynamo\Event\MethodEvent;
use Tebru\Retrofit\Generation\Builder\Factory\MethodBodyBuilderFactory;
use Tebru\Retrofit\Generation\Handler\HandlerInterface;
class DynamoMethodListener
{
/**
* @var HandlerInterface[]
*/
private $handlers;
/**
* Creates a new method body builder
*
* @var MethodBodyBuilderFactory
*/
private $methodBodyBuilderFactory;
/**
* Constructor
*
* @param HandlerInterface[] $handlers
* @param MethodBodyBuilderFactory $methodBodyBuilderFactory
*/
public function __construct(array $handlers, MethodBodyBuilderFactory $methodBodyBuilderFactory)
{
$this->handlers = $handlers;
$this->methodBodyBuilderFactory = $methodBodyBuilderFactory;
}
/**
* Handler the event
*
* @param MethodEvent $event
*/
public function __invoke(MethodEvent $event)
{
$methodModel = $event->getMethodModel();
$annotations = $event->getAnnotationCollection();
$methodBodyBuilder = $this->methodBodyBuilderFactory->make();
foreach ($this->handlers as $handler) {
$handler->handle($methodModel, $methodBodyBuilder, $annotations);
}
$body = $methodBodyBuilder->build();
$methodModel->setBody($body);
}
}
Option D
Create an aggregate method handler. This wouldn't require a major refactor, but could help keep the method listener simpler. The listener would create a single handler, which would be responsible for creating and calling the other handlers. I haven't fully thought this one through yet...
<?php
namespace Tebru\Retrofit\Generation\Handler;
class MethodHandler
{
/**
* @var Handler[]
*/
private $handlers;
/**
* @param MethodModel $methodModel
* @param MethodBodyBuilder $methodBodyBuilder
* @param AnnotationCollection $annotations
*/
public function __construct(MethodModel $methodModel, MethodBodyBuilder $methodBodyBuilder, AnnotationCollection $annotations);
{
$this->handlers = [
new BaseUrlHandler($methodModel, $methodBodyBuilder, $annotations),
new SerializationContextHandler($methodModel, $methodBodyBuilder, $annotations),
new RequestUrlHandler($methodModel, $methodBodyBuilder, $annotations),
new RequestHeaderHandler($methodModel, $methodBodyBuilder, $annotations),
new RequestBodyHandler($methodModel, $methodBodyBuilder, $annotations),
new ReturnHandler($methodModel, $methodBodyBuilder, $annotations),
new AsyncHandler($methodModel, $methodBodyBuilder, $annotations),
];
}
/**
* @inheritdoc
*/
public function handle()
{
foreach ($this->handlers as $handler) {
$handler->handle();
}
}
}
<?php
namespace Tebru\Retrofit\Generation\Listener;
use Tebru\Dynamo\Event\MethodEvent;
use Tebru\Retrofit\Generation\Builder\Factory\MethodBodyBuilderFactory;
class DynamoMethodListener
{
/**
* Creates a new method body builder
*
* @var MethodBodyBuilderFactory
*/
private $methodBodyBuilderFactory;
/**
* Constructor
*
* @param MethodBodyBuilderFactory $methodBodyBuilderFactory
*/
public function __construct(MethodBodyBuilderFactory $methodBodyBuilderFactory)
{
$this->methodBodyBuilderFactory = $methodBodyBuilderFactory;
}
/**
* Handler the event
*
* @param MethodEvent $event
*/
public function __invoke(MethodEvent $event)
{
$methodModel = $event->getMethodModel();
$annotations = $event->getAnnotationCollection();
$methodBodyBuilder = $this->methodBodyBuilderFactory->make();
$handler = new MethodHandler($methodModel, $methodBodyBuilder, $annotations);
$handler->handle();
$body = $methodBodyBuilder->build();
$methodModel->setBody($body);
}
}
Hello,
firstly thanks for a great job with this java-backport lib.
You have any plans to migrate doctrine annotations to PHP native attributes?
It appears that query map is not correctly creating a query string. Figure out how to correctly merge into query params.
PhpParser/Parser changed to an interface in nikic/php-parser: ^2.0 causing:
Cannot instantiate interface PhpParser\Parser in /bla/bla/bla/vendor/tebru/retrofit-php/src/Generation/Printer/ArrayPrinter.php on line 41
I think changing:
if (null === $parser) {
$parser = new Parser(new Lexer());
}
To either using the PhpParser factory instead:
use PhpParser\ParserFactory;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
or always passing in the parser argument to use in the ArrayPrintyer.
A temporary workaround for this is to (i think) install tebru/dyanmo:0.3.1:
composer require tebru/dyanmo:0.3.1
For some reason every time the annotations were loaded while my cache was empty, they worked. Perhaps because my app was also loading @Route
annotations at the same time.
But once the cache was warmed, every subsequent load failed:
AnnotationException::semanticalError('The annotation "@Tebru\\Retrofit\\Annotation\\GET" in method App\\Azure\\AzureSdkInterface::listDeployments() does not exist, or could not be auto-loaded.')
in DocParser.php line 734
I added this to my index.php
and bin/console
to make it work again:
$loader = require dirname(__DIR__).'/vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
Make it more clear where errors are occurring, and what went wrong.
The Guzzle 5 event emitter doesn't seem to be working in the new (2.x) version of Retrofit.
In some very basic debugging, I found that the Guzzle transaction was not using the EventEmitter that was attached to the Client.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.