123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- <?php
- namespace Mpociot\ApiDoc\Generators;
- use Faker\Factory;
- use Illuminate\Foundation\Http\FormRequest;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Facades\Validator;
- use Illuminate\Support\Str;
- use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description;
- use ReflectionClass;
- use Mpociot\Reflection\DocBlock;
- abstract class AbstractGenerator
- {
- /**
- * @param $route
- *
- * @return mixed
- */
- abstract protected function getUri($route);
- /**
- * @param \Illuminate\Routing\Route $route
- * @param array $bindings
- * @param bool $withResponse
- *
- * @return array
- */
- abstract public function processRoute($route, $bindings = [], $withResponse = true);
- /**
- * @param array $routeData
- * @param array $routeAction
- * @param array $bindings
- *
- * @return mixed
- */
- protected function getParameters($routeData, $routeAction, $bindings)
- {
- $validator = Validator::make([], $this->getRouteRules($routeAction['uses'], $bindings));
- foreach ($validator->getRules() as $attribute => $rules) {
- $attributeData = [
- 'required' => false,
- 'type' => null,
- 'default' => '',
- 'value' => '',
- 'description' => [],
- ];
- foreach ($rules as $ruleName => $rule) {
- $this->parseRule($rule, $attribute, $attributeData, $routeData['id']);
- }
- $routeData['parameters'][$attribute] = $attributeData;
- }
- return $routeData;
- }
- /**
- * @param $route
- * @param $bindings
- * @param $headers
- *
- * @return \Illuminate\Http\Response
- */
- protected function getRouteResponse($route, $bindings, $headers = [])
- {
- $uri = $this->addRouteModelBindings($route, $bindings);
- $methods = $route->getMethods();
- // Split headers into key - value pairs
- $headers = collect($headers)->map(function($value) {
- $split = explode(':', $value);
- return [trim($split[0]) => trim($split[1])];
- })->collapse()->toArray();
- return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers);
- }
- /**
- * @param $route
- * @param array $bindings
- *
- * @return mixed
- */
- protected function addRouteModelBindings($route, $bindings)
- {
- $uri = $this->getUri($route);
- foreach ($bindings as $model => $id) {
- $uri = str_replace('{'.$model.'}', $id, $uri);
- }
- return $uri;
- }
- /**
- * @param \Illuminate\Routing\Route $route
- *
- * @return string
- */
- protected function getRouteDescription($route)
- {
- list($class, $method) = explode('@', $route);
- $reflection = new ReflectionClass($class);
- $reflectionMethod = $reflection->getMethod($method);
- $comment = $reflectionMethod->getDocComment();
- $phpdoc = new DocBlock($comment);
- return [
- 'short' => $phpdoc->getShortDescription(),
- 'long' => $phpdoc->getLongDescription()->getContents(),
- ];
- }
- /**
- * @param string $route
- *
- * @return string
- */
- protected function getRouteGroup($route)
- {
- list($class, $method) = explode('@', $route);
- $reflection = new ReflectionClass($class);
- $comment = $reflection->getDocComment();
- if ($comment) {
- $phpdoc = new DocBlock($comment);
- foreach ($phpdoc->getTags() as $tag) {
- if ($tag->getName() === 'resource') {
- return $tag->getContent();
- }
- }
- }
- return 'general';
- }
- /**
- * @param $route
- * @param array $bindings
- *
- * @return array
- */
- protected function getRouteRules($route, $bindings)
- {
- list($class, $method) = explode('@', $route);
- $reflection = new ReflectionClass($class);
- $reflectionMethod = $reflection->getMethod($method);
- foreach ($reflectionMethod->getParameters() as $parameter) {
- $parameterType = $parameter->getClass();
- if (! is_null($parameterType) && class_exists($parameterType->name)) {
- $className = $parameterType->name;
- if (is_subclass_of($className, FormRequest::class)) {
- $parameterReflection = new $className;
- // Add route parameter bindings
- $parameterReflection->query->add($bindings);
- $parameterReflection->request->add($bindings);
- if (method_exists($parameterReflection, 'validator')) {
- return $parameterReflection->validator()->getRules();
- } else {
- return $parameterReflection->rules();
- }
- }
- }
- }
- return [];
- }
- /**
- * @param array $arr
- * @param string $first
- * @param string $last
- *
- * @return string
- */
- protected function fancyImplode($arr, $first, $last)
- {
- $arr = array_map(function ($value) {
- return '`'.$value.'`';
- }, $arr);
- array_push($arr, implode($last, array_splice($arr, -2)));
- return implode($first, $arr);
- }
- protected function splitValuePairs($parameters, $first = 'is ', $last = 'or ') {
- $attribute = '';
- collect($parameters)->map(function($item, $key) use (&$attribute, $first, $last) {
- $attribute .= '`'.$item.'` ';
- if (($key+1) % 2 === 0) {
- $attribute .= $last;
- } else {
- $attribute .= $first;
- }
- });
- $attribute = rtrim($attribute, $last);
- return $attribute;
- }
- /**
- * @param string $rule
- * @param string $ruleName
- * @param array $attributeData
- * @param int $seed
- *
- * @return void
- */
- protected function parseRule($rule, $ruleName, &$attributeData, $seed)
- {
- $faker = Factory::create();
- $faker->seed(crc32($seed));
- $parsedRule = $this->parseStringRule($rule);
- $parsedRule[0] = $this->normalizeRule($parsedRule[0]);
- list($rule, $parameters) = $parsedRule;
- switch ($rule) {
- case 'required':
- $attributeData['required'] = true;
- break;
- case 'accepted':
- $attributeData['required'] = true;
- $attributeData['type'] = 'boolean';
- $attributeData['value'] = true;
- break;
- case 'after':
- $attributeData['type'] = 'date';
- $attributeData['description'][] = Description::parse($rule)->with(date(DATE_RFC850, strtotime($parameters[0])))->getDescription();
- $attributeData['value'] = date(DATE_RFC850, strtotime('+1 day', strtotime($parameters[0])));
- break;
- case 'alpha':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- $attributeData['value'] = $faker->word;
- break;
- case 'alpha_dash':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'alpha_num':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'in':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- $attributeData['value'] = $faker->randomElement($parameters);
- break;
- case 'not_in':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- $attributeData['value'] = $faker->word;
- break;
- case 'min':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- if (Arr::get($attributeData, 'type') === 'numeric' || Arr::get($attributeData, 'type') === 'integer') {
- $attributeData['value'] = $faker->numberBetween($parameters[0]);
- }
- break;
- case 'max':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- if (Arr::get($attributeData, 'type') === 'numeric' || Arr::get($attributeData, 'type') === 'integer') {
- $attributeData['value'] = $faker->numberBetween(0, $parameters[0]);
- }
- break;
- case 'between':
- if (! isset($attributeData['type'])) {
- $attributeData['type'] = 'numeric';
- }
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- $attributeData['value'] = $faker->numberBetween($parameters[0], $parameters[1]);
- break;
- case 'before':
- $attributeData['type'] = 'date';
- $attributeData['description'][] = Description::parse($rule)->with(date(DATE_RFC850, strtotime($parameters[0])))->getDescription();
- $attributeData['value'] = date(DATE_RFC850, strtotime('-1 day', strtotime($parameters[0])));
- break;
- case 'date_format':
- $attributeData['type'] = 'date';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- $attributeData['value'] = date($parameters[0]);
- break;
- case 'different':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'digits':
- $attributeData['type'] = 'numeric';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- $attributeData['value'] = $faker->randomNumber($parameters[0], true);
- break;
- case 'digits_between':
- $attributeData['type'] = 'numeric';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'file':
- $attributeData['type'] = 'file';
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'image':
- $attributeData['type'] = 'image';
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'json':
- $attributeData['type'] = 'string';
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- $attributeData['value'] = json_encode(['foo', 'bar', 'baz']);
- break;
- case 'mimetypes':
- case 'mimes':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- break;
- case 'required_if':
- $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription();
- break;
- case 'required_unless':
- $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription();
- break;
- case 'required_with':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- break;
- case 'required_with_all':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
- break;
- case 'required_without':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- break;
- case 'required_without_all':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
- break;
- case 'same':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'size':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'timezone':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- $attributeData['value'] = $faker->timezone;
- break;
- case 'exists':
- $fieldName = isset($parameters[1]) ? $parameters[1] : $ruleName;
- $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $fieldName])->getDescription();
- break;
- case 'active_url':
- $attributeData['type'] = 'url';
- $attributeData['value'] = $faker->url;
- break;
- case 'regex':
- $attributeData['type'] = 'string';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'boolean':
- $attributeData['value'] = true;
- $attributeData['type'] = $rule;
- break;
- case 'array':
- $attributeData['value'] = $faker->word;
- $attributeData['type'] = $rule;
- break;
- case 'date':
- $attributeData['value'] = $faker->date();
- $attributeData['type'] = $rule;
- break;
- case 'email':
- $attributeData['value'] = $faker->safeEmail;
- $attributeData['type'] = $rule;
- break;
- case 'string':
- $attributeData['value'] = $faker->word;
- $attributeData['type'] = $rule;
- break;
- case 'integer':
- $attributeData['value'] = $faker->randomNumber();
- $attributeData['type'] = $rule;
- break;
- case 'numeric':
- $attributeData['value'] = $faker->randomNumber();
- $attributeData['type'] = $rule;
- break;
- case 'url':
- $attributeData['value'] = $faker->url;
- $attributeData['type'] = $rule;
- break;
- case 'ip':
- $attributeData['value'] = $faker->ipv4;
- $attributeData['type'] = $rule;
- break;
- }
- if ($attributeData['value'] === '') {
- $attributeData['value'] = $faker->word;
- }
- if (is_null($attributeData['type'])) {
- $attributeData['type'] = 'string';
- }
- }
- /**
- * Call the given URI and return the Response.
- *
- * @param string $method
- * @param string $uri
- * @param array $parameters
- * @param array $cookies
- * @param array $files
- * @param array $server
- * @param string $content
- *
- * @return \Illuminate\Http\Response
- */
- abstract public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null);
- /**
- * Transform headers array to array of $_SERVER vars with HTTP_* format.
- *
- * @param array $headers
- *
- * @return array
- */
- protected function transformHeadersToServerVars(array $headers)
- {
- $server = [];
- $prefix = 'HTTP_';
- foreach ($headers as $name => $value) {
- $name = strtr(strtoupper($name), '-', '_');
- if (! Str::startsWith($name, $prefix) && $name !== 'CONTENT_TYPE') {
- $name = $prefix.$name;
- }
- $server[$name] = $value;
- }
- return $server;
- }
- /**
- * Parse a string based rule.
- *
- * @param string $rules
- *
- * @return array
- */
- protected function parseStringRule($rules)
- {
- $parameters = [];
- // The format for specifying validation rules and parameters follows an
- // easy {rule}:{parameters} formatting convention. For instance the
- // rule "Max:3" states that the value may only be three letters.
- if (strpos($rules, ':') !== false) {
- list($rules, $parameter) = explode(':', $rules, 2);
- $parameters = $this->parseParameters($rules, $parameter);
- }
- return [strtolower(trim($rules)), $parameters];
- }
- /**
- * Parse a parameter list.
- *
- * @param string $rule
- * @param string $parameter
- *
- * @return array
- */
- protected function parseParameters($rule, $parameter)
- {
- if (strtolower($rule) === 'regex') {
- return [$parameter];
- }
- return str_getcsv($parameter);
- }
- /**
- * Normalizes a rule so that we can accept short types.
- *
- * @param string $rule
- *
- * @return string
- */
- protected function normalizeRule($rule)
- {
- switch ($rule) {
- case 'int':
- return 'integer';
- case 'bool':
- return 'boolean';
- default:
- return $rule;
- }
- }
- }
|