{"action":"created","comment":{"url":"https://api.github.com/repos/simpspector/analyser/pulls/comments/32090365","id":32090365,"diff_hunk":"@@ -0,0 +1,349 @@\n+<?php\n+\n+namespace SimpSpector\\Analyser\\Gadget;\n+\n+use SimpSpector\\Analyser\\Gadget\\DocBlockGadget\\FunctionParameter;\n+use SimpSpector\\Analyser\\Gadget\\DocBlockGadget\\FunctionSignature;\n+use phpDocumentor\\Reflection\\DocBlock;\n+use SimpSpector\\Analyser\\Issue;\n+use SimpSpector\\Analyser\\Logger\\AbstractLogger;\n+use SimpSpector\\Analyser\\Result;\n+use SimpSpector\\Analyser\\Util\\FilesystemHelper;\n+use Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition;\n+\n+class DocBlockGadget implements GadgetInterface\n+{\n+    /**\n+     * @param ArrayNodeDefinition $node\n+     */\n+    public function configure(ArrayNodeDefinition $node)\n+    {\n+        $node->children()\n+            ->node('files', 'paths')->defaultValue(['./'])->end()\n+            ->node('missing_docblock', 'nullable_level')->defaultValue(Issue::LEVEL_ERROR)->end()\n+            ->node('missing_newline_before_return', 'nullable_level')->defaultValue(Issue::LEVEL_NOTICE)->end()\n+            ->node('obsolete_variable', 'nullable_level')->defaultValue(Issue::LEVEL_NOTICE)->end()\n+            ->node('missing_variable', 'nullable_level')->defaultValue(Issue::LEVEL_NOTICE)->end()\n+            ->node('type_missmatch', 'nullable_level')->defaultValue(Issue::LEVEL_NOTICE)->end()\n+        ->end();\n+    }\n+\n+    /**\n+     * @param string $path\n+     * @param array $options\n+     * @param AbstractLogger $logger\n+     *\n+     * @return Result\n+     */\n+    public function run($path, array $options, AbstractLogger $logger)\n+    {\n+        $result = new Result();\n+        $files  = FilesystemHelper::findFiles($path, $options['files']);\n+        foreach ($files as $file) {\n+            foreach ((new \\PHP_Token_Stream($file))->getClasses() as $className => $class) {\n+                foreach ($class['methods'] as $method) {\n+                    $result->merge($this->processMethod($options, $method, $className, $file));\n+                }\n+            }\n+        }\n+\n+        return $result;\n+\n+    }\n+\n+    /**\n+     * @return string\n+     */\n+    public function getName()\n+    {\n+        return 'doc_block';\n+    }\n+\n+    /**\n+     * @param array $options\n+     * @param array $method\n+     * @param string $className\n+     * @param string $file\n+     *\n+     * @return Result\n+     */\n+    private function processMethod(array $options, array $method, $className, $file)\n+    {\n+        $result = new Result();\n+        if ($method['docblock'] === null) {\n+            if ($options['missing_docblock'] !== null) {\n+                $result->addIssue($this->createIssueForMissingDocblock($options, $method, $className, $file));\n+            }\n+        } else {\n+            $docBlock = new DocBlock($method['docblock']);\n+\n+            $tagsInDocblock = $docBlock->getTags();\n+            $signature      = new FunctionSignature($method['signature']);\n+\n+            if ($options['type_missmatch'] !== null) {\n+                foreach ($this->inBoth($tagsInDocblock, $signature) as $params) {\n+\n+                    /** @var DocBlock\\Tag\\ParamTag $docBlockParam */\n+                    $docBlockParam = $params[0];\n+                    /** @var FunctionParameter $functionParameter */\n+                    $functionParameter = $params[1];\n+\n+                    if (! $this->equalTypes($functionParameter, $docBlockParam)) {\n+                        $result->addIssue($this->createIssueForMismatchingTypes(\n+                            $options, $method, $className, $file, $functionParameter, $docBlockParam->getType()\n+                        ));\n+                    }\n+                }\n+            }\n+            if ($options['obsolete_variable'] !== null) {\n+                foreach ($this->inDocblockOnly($tagsInDocblock, $signature) as $param) {\n+                    $result->addIssue($this->createIssueForObsoleteVariable(\n+                        $options, $method, $className, $file, $param->getContent())\n+                    );\n+                }\n+            }\n+            if ($options['missing_variable'] !== null) {\n+                foreach ($this->inSignatureOnly($tagsInDocblock, $signature) as $param) {\n+                    $result->addIssue($this->createIssueForMissingVariable(\n+                        $options, $method, $className, $file, $param\n+                    ));\n+                }\n+            }\n+        }\n+\n+        return $result;\n+    }\n+\n+    /**\n+     * @param array $tagsInDocblock\n+     * @param FunctionSignature $signature\n+     *\n+     * @return array[]\n+     */\n+    private function inBoth(array $tagsInDocblock, FunctionSignature $signature)\n+    {\n+        $signatureParametersByName = [];\n+        foreach ($signature->parameters as $parameter) {\n+            $signatureParametersByName[$parameter->name] = $parameter;\n+        }\n+\n+        /** @var DocBlock\\Tag\\ParamTag[] $docblockParams */\n+        $docblockParams = array_filter(\n+            $tagsInDocblock,\n+            function ($tag) use ($signature) {\n+                return (\n+                    ($tag instanceof DocBlock\\Tag\\ParamTag)\n+                    && in_array($tag->getVariableName(), $this->getFunctionParameterNames($signature))\n+                );\n+            }\n+        );\n+\n+        $ret = [];\n+        foreach ($docblockParams as $parameter) {\n+            $ret[] = [\n+                $parameter,\n+                $signatureParametersByName[$parameter->getVariableName()]\n+            ];\n+        }\n+\n+        return $ret;\n+    }\n+\n+    /**\n+     * @param array $tagsInDocblock\n+     * @param FunctionSignature $signature\n+     *\n+     * @return DocBlock\\Tag\\ParamTag[]\n+     */\n+    private function inDocblockOnly(array $tagsInDocblock, FunctionSignature $signature)\n+    {\n+        return array_filter(\n+            $tagsInDocblock,\n+            function ($tag) use ($signature) {\n+                return (\n+                    ($tag instanceof DocBlock\\Tag\\ParamTag)\n+                    && ! in_array($tag->getVariableName(), $this->getFunctionParameterNames($signature))\n+                );\n+            }\n+        );\n+    }\n+\n+    /**\n+     * @param array $tagsInDocblock\n+     * @param FunctionSignature $signature\n+     *\n+     * @return FunctionParameter[]\n+     */\n+    private function inSignatureOnly(array $tagsInDocblock, FunctionSignature $signature)\n+    {\n+        return array_filter(\n+            $signature->parameters,\n+            function (FunctionParameter $functionParameter) use ($tagsInDocblock) {\n+                return ! in_array($functionParameter->name, $this->getParameterNames($tagsInDocblock));\n+            }\n+        );\n+    }\n+\n+    /**\n+     * @param array $tagsInDocblock\n+     *\n+     * @return string[]\n+     */\n+    private function getParameterNames(array $tagsInDocblock)\n+    {\n+        return array_map(\n+            function (DocBlock\\Tag\\ParamTag $tag) {\n+                return $tag->getVariableName();\n+            },\n+            array_filter(\n+                $tagsInDocblock,\n+                function ($tag) {\n+                    return $tag instanceof DocBlock\\Tag\\ParamTag;\n+                }\n+            )\n+        );\n+    }\n+\n+    /**\n+     * @param FunctionSignature $signature\n+     *\n+     * @return array\n+     */\n+    private function getFunctionParameterNames(FunctionSignature $signature)\n+    {\n+        return array_map(\n+            function (FunctionParameter $parameter) {\n+                return $parameter->name;\n+            },\n+            $signature->parameters\n+        );\n+    }\n+\n+    /**\n+     * @param FunctionParameter $functionParameter\n+     * @param DocBlock\\Tag\\ParamTag $docBlockParam\n+     *\n+     * @return bool\n+     */\n+    private function equalTypes(FunctionParameter $functionParameter, DocBlock\\Tag\\ParamTag $docBlockParam)\n+    {\n+        return $functionParameter->type == ltrim($docBlockParam->getType(), '\\\\');\n+    }\n+\n+    /**\n+     * @param array $options\n+     * @param array $method\n+     * @param string $className\n+     * @param string $file\n+     *\n+     * @return Issue\n+     */\n+    private function createIssueForMissingDocblock(array $options, array $method, $className, $file)\n+    {","path":"src/Gadget/DocBlockGadget.php","position":242,"original_position":242,"commit_id":"42f66b538610c4070491e73957b87059d15fabec","original_commit_id":"42f66b538610c4070491e73957b87059d15fabec","user":{"login":"tolry","id":259744,"avatar_url":"https://avatars.githubusercontent.com/u/259744?v=3","gravatar_id":"","url":"https://api.github.com/users/tolry","html_url":"https://github.com/tolry","followers_url":"https://api.github.com/users/tolry/followers","following_url":"https://api.github.com/users/tolry/following{/other_user}","gists_url":"https://api.github.com/users/tolry/gists{/gist_id}","starred_url":"https://api.github.com/users/tolry/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tolry/subscriptions","organizations_url":"https://api.github.com/users/tolry/orgs","repos_url":"https://api.github.com/users/tolry/repos","events_url":"https://api.github.com/users/tolry/events{/privacy}","received_events_url":"https://api.github.com/users/tolry/received_events","type":"User","site_admin":false},"body":"forgot to check if `$method['missing_docblock'] == null` and exit early? same goes for the following methods","created_at":"2015-06-10T06:04:42Z","updated_at":"2015-06-10T06:04:42Z","html_url":"https://github.com/simpspector/analyser/pull/34#discussion_r32090365","pull_request_url":"https://api.github.com/repos/simpspector/analyser/pulls/34","_links":{"self":{"href":"https://api.github.com/repos/simpspector/analyser/pulls/comments/32090365"},"html":{"href":"https://github.com/simpspector/analyser/pull/34#discussion_r32090365"},"pull_request":{"href":"https://api.github.com/repos/simpspector/analyser/pulls/34"}}},"pull_request":{"url":"https://api.github.com/repos/simpspector/analyser/pulls/34","id":37178677,"html_url":"https://github.com/simpspector/analyser/pull/34","diff_url":"https://github.com/simpspector/analyser/pull/34.diff","patch_url":"https://github.com/simpspector/analyser/pull/34.patch","issue_url":"https://api.github.com/repos/simpspector/analyser/issues/34","number":34,"state":"open","locked":false,"title":"Feature/doc blocks","user":{"login":"larsborn","id":1826897,"avatar_url":"https://avatars.githubusercontent.com/u/1826897?v=3","gravatar_id":"","url":"https://api.github.com/users/larsborn","html_url":"https://github.com/larsborn","followers_url":"https://api.github.com/users/larsborn/followers","following_url":"https://api.github.com/users/larsborn/following{/other_user}","gists_url":"https://api.github.com/users/larsborn/gists{/gist_id}","starred_url":"https://api.github.com/users/larsborn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larsborn/subscriptions","organizations_url":"https://api.github.com/users/larsborn/orgs","repos_url":"https://api.github.com/users/larsborn/repos","events_url":"https://api.github.com/users/larsborn/events{/privacy}","received_events_url":"https://api.github.com/users/larsborn/received_events","type":"User","site_admin":false},"body":"fix #13 ","created_at":"2015-06-08T17:51:44Z","updated_at":"2015-06-10T06:04:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a81f5a5d680439d3bbac1bf8a7af51f623dd62dd","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/simpspector/analyser/pulls/34/commits","review_comments_url":"https://api.github.com/repos/simpspector/analyser/pulls/34/comments","review_comment_url":"https://api.github.com/repos/simpspector/analyser/pulls/comments{/number}","comments_url":"https://api.github.com/repos/simpspector/analyser/issues/34/comments","statuses_url":"https://api.github.com/repos/simpspector/analyser/statuses/42f66b538610c4070491e73957b87059d15fabec","head":{"label":"simpspector:feature/doc-blocks","ref":"feature/doc-blocks","sha":"42f66b538610c4070491e73957b87059d15fabec","user":{"login":"simpspector","id":11267955,"avatar_url":"https://avatars.githubusercontent.com/u/11267955?v=3","gravatar_id":"","url":"https://api.github.com/users/simpspector","html_url":"https://github.com/simpspector","followers_url":"https://api.github.com/users/simpspector/followers","following_url":"https://api.github.com/users/simpspector/following{/other_user}","gists_url":"https://api.github.com/users/simpspector/gists{/gist_id}","starred_url":"https://api.github.com/users/simpspector/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simpspector/subscriptions","organizations_url":"https://api.github.com/users/simpspector/orgs","repos_url":"https://api.github.com/users/simpspector/repos","events_url":"https://api.github.com/users/simpspector/events{/privacy}","received_events_url":"https://api.github.com/users/simpspector/received_events","type":"Organization","site_admin":false},"repo":{"id":30931414,"name":"analyser","full_name":"simpspector/analyser","owner":{"login":"simpspector","id":11267955,"avatar_url":"https://avatars.githubusercontent.com/u/11267955?v=3","gravatar_id":"","url":"https://api.github.com/users/simpspector","html_url":"https://github.com/simpspector","followers_url":"https://api.github.com/users/simpspector/followers","following_url":"https://api.github.com/users/simpspector/following{/other_user}","gists_url":"https://api.github.com/users/simpspector/gists{/gist_id}","starred_url":"https://api.github.com/users/simpspector/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simpspector/subscriptions","organizations_url":"https://api.github.com/users/simpspector/orgs","repos_url":"https://api.github.com/users/simpspector/repos","events_url":"https://api.github.com/users/simpspector/events{/privacy}","received_events_url":"https://api.github.com/users/simpspector/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/simpspector/analyser","description":"this is the analyser component to simpspector/simpspector - all the static code inspection is done here","fork":false,"url":"https://api.github.com/repos/simpspector/analyser","forks_url":"https://api.github.com/repos/simpspector/analyser/forks","keys_url":"https://api.github.com/repos/simpspector/analyser/keys{/key_id}","collaborators_url":"https://api.github.com/repos/simpspector/analyser/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/simpspector/analyser/teams","hooks_url":"https://api.github.com/repos/simpspector/analyser/hooks","issue_events_url":"https://api.github.com/repos/simpspector/analyser/issues/events{/number}","events_url":"https://api.github.com/repos/simpspector/analyser/events","assignees_url":"https://api.github.com/repos/simpspector/analyser/assignees{/user}","branches_url":"https://api.github.com/repos/simpspector/analyser/branches{/branch}","tags_url":"https://api.github.com/repos/simpspector/analyser/tags","blobs_url":"https://api.github.com/repos/simpspector/analyser/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/simpspector/analyser/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/simpspector/analyser/git/refs{/sha}","trees_url":"https://api.github.com/repos/simpspector/analyser/git/trees{/sha}","statuses_url":"https://api.github.com/repos/simpspector/analyser/statuses/{sha}","languages_url":"https://api.github.com/repos/simpspector/analyser/languages","stargazers_url":"https://api.github.com/repos/simpspector/analyser/stargazers","contributors_url":"https://api.github.com/repos/simpspector/analyser/contributors","subscribers_url":"https://api.github.com/repos/simpspector/analyser/subscribers","subscription_url":"https://api.github.com/repos/simpspector/analyser/subscription","commits_url":"https://api.github.com/repos/simpspector/analyser/commits{/sha}","git_commits_url":"https://api.github.com/repos/simpspector/analyser/git/commits{/sha}","comments_url":"https://api.github.com/repos/simpspector/analyser/comments{/number}","issue_comment_url":"https://api.github.com/repos/simpspector/analyser/issues/comments{/number}","contents_url":"https://api.github.com/repos/simpspector/analyser/contents/{+path}","compare_url":"https://api.github.com/repos/simpspector/analyser/compare/{base}...{head}","merges_url":"https://api.github.com/repos/simpspector/analyser/merges","archive_url":"https://api.github.com/repos/simpspector/analyser/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/simpspector/analyser/downloads","issues_url":"https://api.github.com/repos/simpspector/analyser/issues{/number}","pulls_url":"https://api.github.com/repos/simpspector/analyser/pulls{/number}","milestones_url":"https://api.github.com/repos/simpspector/analyser/milestones{/number}","notifications_url":"https://api.github.com/repos/simpspector/analyser/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/simpspector/analyser/labels{/name}","releases_url":"https://api.github.com/repos/simpspector/analyser/releases{/id}","created_at":"2015-02-17T18:40:04Z","updated_at":"2015-03-30T11:44:09Z","pushed_at":"2015-06-09T23:32:54Z","git_url":"git://github.com/simpspector/analyser.git","ssh_url":"git@github.com:simpspector/analyser.git","clone_url":"https://github.com/simpspector/analyser.git","svn_url":"https://github.com/simpspector/analyser","homepage":"","size":472,"stargazers_count":2,"watchers_count":2,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":11,"forks":0,"open_issues":11,"watchers":2,"default_branch":"master"}},"base":{"label":"simpspector:master","ref":"master","sha":"e1b89b0a82037387d78628358f5cc6dc3674cbf9","user":{"login":"simpspector","id":11267955,"avatar_url":"https://avatars.githubusercontent.com/u/11267955?v=3","gravatar_id":"","url":"https://api.github.com/users/simpspector","html_url":"https://github.com/simpspector","followers_url":"https://api.github.com/users/simpspector/followers","following_url":"https://api.github.com/users/simpspector/following{/other_user}","gists_url":"https://api.github.com/users/simpspector/gists{/gist_id}","starred_url":"https://api.github.com/users/simpspector/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simpspector/subscriptions","organizations_url":"https://api.github.com/users/simpspector/orgs","repos_url":"https://api.github.com/users/simpspector/repos","events_url":"https://api.github.com/users/simpspector/events{/privacy}","received_events_url":"https://api.github.com/users/simpspector/received_events","type":"Organization","site_admin":false},"repo":{"id":30931414,"name":"analyser","full_name":"simpspector/analyser","owner":{"login":"simpspector","id":11267955,"avatar_url":"https://avatars.githubusercontent.com/u/11267955?v=3","gravatar_id":"","url":"https://api.github.com/users/simpspector","html_url":"https://github.com/simpspector","followers_url":"https://api.github.com/users/simpspector/followers","following_url":"https://api.github.com/users/simpspector/following{/other_user}","gists_url":"https://api.github.com/users/simpspector/gists{/gist_id}","starred_url":"https://api.github.com/users/simpspector/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simpspector/subscriptions","organizations_url":"https://api.github.com/users/simpspector/orgs","repos_url":"https://api.github.com/users/simpspector/repos","events_url":"https://api.github.com/users/simpspector/events{/privacy}","received_events_url":"https://api.github.com/users/simpspector/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/simpspector/analyser","description":"this is the analyser component to simpspector/simpspector - all the static code inspection is done here","fork":false,"url":"https://api.github.com/repos/simpspector/analyser","forks_url":"https://api.github.com/repos/simpspector/analyser/forks","keys_url":"https://api.github.com/repos/simpspector/analyser/keys{/key_id}","collaborators_url":"https://api.github.com/repos/simpspector/analyser/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/simpspector/analyser/teams","hooks_url":"https://api.github.com/repos/simpspector/analyser/hooks","issue_events_url":"https://api.github.com/repos/simpspector/analyser/issues/events{/number}","events_url":"https://api.github.com/repos/simpspector/analyser/events","assignees_url":"https://api.github.com/repos/simpspector/analyser/assignees{/user}","branches_url":"https://api.github.com/repos/simpspector/analyser/branches{/branch}","tags_url":"https://api.github.com/repos/simpspector/analyser/tags","blobs_url":"https://api.github.com/repos/simpspector/analyser/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/simpspector/analyser/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/simpspector/analyser/git/refs{/sha}","trees_url":"https://api.github.com/repos/simpspector/analyser/git/trees{/sha}","statuses_url":"https://api.github.com/repos/simpspector/analyser/statuses/{sha}","languages_url":"https://api.github.com/repos/simpspector/analyser/languages","stargazers_url":"https://api.github.com/repos/simpspector/analyser/stargazers","contributors_url":"https://api.github.com/repos/simpspector/analyser/contributors","subscribers_url":"https://api.github.com/repos/simpspector/analyser/subscribers","subscription_url":"https://api.github.com/repos/simpspector/analyser/subscription","commits_url":"https://api.github.com/repos/simpspector/analyser/commits{/sha}","git_commits_url":"https://api.github.com/repos/simpspector/analyser/git/commits{/sha}","comments_url":"https://api.github.com/repos/simpspector/analyser/comments{/number}","issue_comment_url":"https://api.github.com/repos/simpspector/analyser/issues/comments{/number}","contents_url":"https://api.github.com/repos/simpspector/analyser/contents/{+path}","compare_url":"https://api.github.com/repos/simpspector/analyser/compare/{base}...{head}","merges_url":"https://api.github.com/repos/simpspector/analyser/merges","archive_url":"https://api.github.com/repos/simpspector/analyser/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/simpspector/analyser/downloads","issues_url":"https://api.github.com/repos/simpspector/analyser/issues{/number}","pulls_url":"https://api.github.com/repos/simpspector/analyser/pulls{/number}","milestones_url":"https://api.github.com/repos/simpspector/analyser/milestones{/number}","notifications_url":"https://api.github.com/repos/simpspector/analyser/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/simpspector/analyser/labels{/name}","releases_url":"https://api.github.com/repos/simpspector/analyser/releases{/id}","created_at":"2015-02-17T18:40:04Z","updated_at":"2015-03-30T11:44:09Z","pushed_at":"2015-06-09T23:32:54Z","git_url":"git://github.com/simpspector/analyser.git","ssh_url":"git@github.com:simpspector/analyser.git","clone_url":"https://github.com/simpspector/analyser.git","svn_url":"https://github.com/simpspector/analyser","homepage":"","size":472,"stargazers_count":2,"watchers_count":2,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":11,"forks":0,"open_issues":11,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/simpspector/analyser/pulls/34"},"html":{"href":"https://github.com/simpspector/analyser/pull/34"},"issue":{"href":"https://api.github.com/repos/simpspector/analyser/issues/34"},"comments":{"href":"https://api.github.com/repos/simpspector/analyser/issues/34/comments"},"review_comments":{"href":"https://api.github.com/repos/simpspector/analyser/pulls/34/comments"},"review_comment":{"href":"https://api.github.com/repos/simpspector/analyser/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/simpspector/analyser/pulls/34/commits"},"statuses":{"href":"https://api.github.com/repos/simpspector/analyser/statuses/42f66b538610c4070491e73957b87059d15fabec"}}}}