Compare commits

...
67 Commits
Author SHA1 Message Date
Barry vd. Heuvel b91b959364 Create FUNDING.yml 2019-10-30 21:53:27 +01:00
naneri 9b0fbb4467 Added table of contents to Readme.md (#833) 2019-10-02 17:32:49 +02:00
Chris Morrell 6c8e847430 Add support for factory()->create() return types (#758)
* Revert "Forked release"

* Initial factory builder support

* Oops — fixed tabs

* Revert "Forked release"

* Whitespace

* Reverted more code

* Whitespace

* More whitespace

* Fixed PHPCS line
2019-10-01 20:18:19 +02:00
cxlblm 25b61c56fe __toString function has been DEPRECATED (#801)
* __toString function has been DEPRECATED

__toString function has been DEPRECATED as of PHP 7.1.0

* remove code

remove code
2019-09-17 07:08:26 +02:00
Alex 428d32cebd Add hasOneThrough to ide-helper:models (#807)
Added missing relation type.
2019-09-16 14:33:30 +02:00
Matthew Hailwood 51a72d7766 [PSR-12] Update param type array to use short array syntax (#828)
* Update Param type array to use short array syntax (PSR-12)

* Update Param type array to use short array syntax (PSR-12)
2019-09-16 11:52:59 +02:00
Patompong Savaengsuk d200f2284b Update readme.md (#825)
* Update readme.md

In version 2.6.5, this commit https://github.com/barryvdh/laravel-ide-helper/commit/f8a8cb18e0a42ae4131b7ed865fe792f9b34630c made it so library's consumer no longer need to pull in `doctrine/dbal` manually. I think we should mention that in the readme file as well. Although I'm not sure if it adds any value to the file itself or not.

* Remove note about doctrine/dbal from readme.md
2019-09-11 08:25:44 +02:00
Давид 8faae38655 Update readme.md (#824) 2019-09-10 15:46:31 +02:00
Barry vd. Heuvel 8740a9a158 Fix CS 2019-09-08 11:56:38 +02:00
liquid207 d344cdc842 Fix phpcs (#803) 2019-09-08 11:55:17 +02:00
Mandy Schoep 5e411ef104 Do not added instances of a anonymous class to the phpstorm_meta file (#806) 2019-09-08 11:46:25 +02:00
Barry vd. Heuvel 499a2113b3 Check if method exists 2019-09-08 11:45:23 +02:00
Barry vd. Heuvel f8a8cb18e0 Always require doctrine 2019-09-08 11:36:40 +02:00
Barry vd. Heuvel 16eb4f65ee Update readme.md 2019-09-03 19:51:13 +02:00
Kevin Bond 0edf8f750e remove monolog configuration (fixes #818) (#820) 2019-09-03 19:49:46 +02:00
Johan van Helden 1b15d2930b fix the identation in the config (#817) 2019-08-31 12:53:08 +02:00
Matthew Hailwood 3668e3821a Add support for custom cast classes (#816)
* Add support for custom cast classes

* Fix unrelated code style warning.
2019-08-30 07:32:01 +02:00
Barry vd. Heuvel 7b4cef1ea4 Update composer.json 2019-08-23 14:21:37 +02:00
Stanislas cd2a71d43c ModelsCommand: Remove question mark (#812)
Sorry this is a bit nitpicky 😅
2019-08-06 07:54:53 +02:00
edvordo c9132b28a6 Chages to x2Many relationships detection & adds relationship_count docBlock comment (#783)
* x to Many relationships are detected via a `Many` keyword in the relationship object class name
   * reason: example: `belongsTo` was incorrectly recognized as `belongsToMany`
 * adds `@property-read int|null $x2Many_relationship_method_name_count` model class docBlock comment
   * when model makes use of `withCount` or `loadCount` there is a property on the resource `$instance->relationship_method_count` that states the count of related models
   * can be `NULL` when `withCount` or `loadCount` aren't used
2019-06-18 16:38:42 +02:00
Guilherme Pressutto 8ab6e0536b Fixed meta not working with app()->makeWith() (#791) 2019-05-29 17:09:57 +02:00
Theodore R. Smith 87ebe458e9 Removed confusing README installation instructions (#786)
While installing this for my project, I didn't see the `--dev` instructions until I was literally editing this README for a pull request.

Since laravel-ide-helper is only designed for development purposes, let's just remove the superfluous entry altogether. Smart devs who for some crazy reason need it on prod will know to remove the `--dev`, while less knowledgeable devs won't shoot themselves in the foot accidentally.
2019-04-17 22:07:56 +02:00
leo108 9ef14f83be use Throwable instead of Exception (#779) 2019-03-27 05:56:31 +01:00
Brent Roose 39c148ad42 Support for custom builder classes (#782) 2019-03-26 11:38:22 +01:00
Francis Lavoie 2b3c9a2449 Fix BC introduced in #765 (#778)
See my comment https://github.com/barryvdh/laravel-ide-helper/pull/765#issuecomment-471014271:

The change in #765 made anonymous functions get reflected with `ReflectionMethod` instead of `ReflectionFunction`, which makes it lose the doc comment. This means that the generated helpers are missing their return types. This is because Closure is also an object and callable.

To fix it, Closure should be excluded from that new condition.
2019-03-12 11:38:49 +01:00
Matthew Brown 725711022b Make autoload exception more specific (#777)
This is a nice-to-have ([as I'm currently overriding it](https://github.com/psalm/laravel-psalm-plugin/commit/a700c89061d151d1c08851abd93d834f9183534d)), but might help other packages.
2019-03-05 10:24:51 +01:00
neoteknic 881f77156c Fix php 7.3 bug (#772) 2019-03-02 15:15:16 +01:00
neoteknic 0fad511954 Fix Bug PHP 7.3 & L5.8 (#774)
add cast (string) to disable bug ($type can be null)
2019-03-02 15:14:46 +01:00
Stan Daniëls 754bb4d075 Add support for invokable classes as macro function (#765)
Macros can be used with invokable classes, like so.

```
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Response;

class ResponseMacroServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Response::macro('foo', new Foo());
    }
}

class Foo
{
    public function __invoke()
    {
        return 'foobar';
    }
}
```

When running `ide-helper:generate` the following fatal error was thrown.

```  Symfony\Component\Debug\Exception\FatalThrowableError  : ReflectionFunction::__construct() expects parameter 1 to be string, object given```

This commit fixes this error.
2019-02-18 20:54:27 +01:00
edcoreweb bcb881b20d Nested relations detection. (#763) 2019-02-03 16:59:09 +01:00
Tim Philips d00600d769 Include DocBloc’s from classes to allow deeper code inspection (#745)
* Add support to include DocBloc’s from classes to allow further code inspection

* Adjust for PSR2

* Additional fix for PSR2
2019-01-28 21:38:57 +01:00
Ahmed Fathy 858dd40844 Install doctrine/dbal on development (#753) 2019-01-28 21:25:55 +01:00
Kyle 4cd11a4d33 Allow Laravel 5.8 (#754) 2019-01-28 21:24:50 +01:00
Barry vd. Heuvel 5439781e7c Update composer.json 2018-12-19 13:13:32 +01:00
+pikari 190bf07c05 fix: warning when call the instance method (#735) 2018-12-19 13:12:44 +01:00
Vladimir Yuldashev 3d7f124089 prevent loading views if view class not in container (#691) 2018-12-19 13:12:05 +01:00
hubertnnn 32df0a3bc6 Fix: Parametrized relationships prevent generation of metadata (#719) 2018-12-19 13:11:08 +01:00
Patrick 2f8283a746 Optionally write missing Laravel Model DocBlock (#700)
* Fix missing doc-block.

* Fixes a bug where a missing DocBlock caused errors.

* Change Option Shorthand to W
Because it is not allowed to have two char shorthands for options.

* Revert code refactoring.

* Fix copied config header.
2018-12-19 13:10:53 +01:00
Barry vd. Heuvel 2a63b31475 Merge pull request #730 from apollopy/master
if aliases not Redis, don't skip
2018-12-19 13:10:13 +01:00
Barry vd. Heuvel 494d1593c8 Merge pull request #732 from HikaruYasuda/fix/duplicateMacroMethods
fix: Macro methods are duplicated registration.
2018-12-19 13:08:48 +01:00
Barry vd. Heuvel e513fb34dc Merge pull request #743 from JeppeKnockaert/fix-travis-timeout
bugfix - Increase composer update timeout in Travis
2018-12-19 13:08:22 +01:00
Barry vd. Heuvel 5db82752ef Merge pull request #742 from JeppeKnockaert/update-reflection-docblock-to-v2-0-6
Update ReflectionDocBlock to v2.0.6
2018-12-19 13:08:07 +01:00
Jeppe Knockaert 1f555bc194 bugfix - Increase composer update timeout in Travis
The composer update takes about 12 minutes, which is just over the
10 minute timeout in Travis.
2018-12-18 08:55:50 +01:00
Jeppe Knockaert b0ce5325de Update ReflectionDocBlock to v2.0.6
This update improves the parsing of return types, allowing array return
types to be recognized.

This was needed to properly recognize the files and allFiles methods as
duplicates (the function filtering duplicate definitions -
`removeDuplicateMethodsFromPhpDoc` in `Alias.php` - didn't recognise
them as duplicates, because the return type was seen as part of the name).
2018-12-17 18:15:59 +01:00
Barry vd. Heuvel aeae59d0af Merge pull request #740 from JeppeKnockaert/fix-cannot-redeclare-method
bugfix - Prevent redeclaring methods inside Aliases
2018-12-17 17:40:51 +01:00
Jeppe Knockaert 001719245c bugfix - Prevent redeclaring methods inside Aliases
The getMethods function gets called both in the removeDuplicateMethodsFromPhpDoc
function and in helper.php, which causes the method detection to be performed twice.

Parts of the method detection includes a check for duplicate methods, but Macro
methods for example, aren't checked for duplicates.

Instead of checking everywhere if we're not adding duplicates, I've included a
check in the getMethods call to just use the already generated methods
if they have been generated already.
2018-12-12 14:46:18 +01:00
Barry vd. Heuvel 99226c92b7 Merge pull request #736 from JeppeKnockaert/fix-real-name-for-closures
bugfix - Do not use the real_name for closures
2018-12-11 12:35:36 +01:00
Jeppe Knockaert dda63bf8df bugfix - Do not use the real_name for closures
Closures do not have a method name that makes sense to use in the ide_helper
file.

e.g. Illuminate\Foundation\Providers\{closure} would be the real_name instead of
just validate. So for the closure methods, we should just use the provided
methodName instead.
2018-12-10 14:14:34 +01:00
HikaruYasuda 3d7f45479e fix: Macro methods are duplicated registration. 2018-12-04 10:33:00 +09:00
ApolloPY 7905dd3926 if aliases not Redis, don't skip 2018-11-30 20:30:51 +08:00
ApolloPY 8d135a1dea Merge pull request #1 from barryvdh/master
merge master
2018-11-30 20:26:36 +08:00
Barry vd. Heuvel 858e730c00 Merge pull request #720 from JeppeKnockaert/remove-duplicate-functions-from-phpdoc
Prevent duplicate function declarations in docblocks
2018-11-07 21:27:14 +01:00
Jeppe Knockaert 2ab961e734 Prevent duplicate function declarations in docblocks
When a method is declared both in the phpdoc and in the class itself,
PhpStorm (2018.3) complains about duplicate function declarations, preventing
correct code hinting.

To solve this, I remove the already declared functions from the generated dockblocks.
2018-11-07 20:59:15 +01:00
Barry vd. Heuvel 981ff45b43 Merge pull request #707 from crhg/fix_getAliases
Fix alias class existence determination #698
2018-10-06 11:35:51 +02:00
Barry vd. Heuvel 8174730714 Merge pull request #714 from pahan35/fix-missed-autocomplete-in-header-view
Fix missed autocomplete in helper view
2018-10-06 11:34:14 +02:00
Barry vd. Heuvel ae5b623cf2 Merge pull request #713 from pahan35/fix-missed-methods-with-same-name
Fix missed methods with same name
2018-10-06 11:33:25 +02:00
Pavlo Zhukov eff51d41b3 Add missed getRealName() call for $namespaces_by_alias group 2018-10-01 03:25:34 +03:00
Pavlo Zhukov b14d983f74 Change comments style to allow folding and make its clear 2018-10-01 00:50:28 +03:00
Pavlo Zhukov 1fdc866fb0 Fix missed autocomplete in helper view 2018-10-01 00:39:40 +03:00
Pavlo Zhukov d7359b86d4 Avoid code duplication in constructors 2018-10-01 00:20:54 +03:00
Pavlo Zhukov 5d8d93273f Fix missed helpers for abstract methods the same name but different classes 2018-10-01 00:02:03 +03:00
Manabu Matsui 30e5300e49 Fix alias class existence determination 2018-09-14 10:16:13 +09:00
Barry vd. Heuvel 593e0d4aa7 Merge pull request #701 from brendt/master
Add ::query support
2018-09-07 12:07:33 +02:00
Brent Roose e7d4cb322f Update ModelsCommand.php 2018-09-07 10:29:59 +02:00
Brent Roose 5318f485e2 Add ::query support 2018-09-07 10:27:20 +02:00
Barry vd. Heuvel 7db1843473 Remove Eloquent mixin thing 2018-09-06 20:41:09 +02:00
Barry vd. Heuvel 1989d0e794 Update .travis.yml 2018-09-04 16:19:48 +02:00
17 changed files with 413 additions and 133 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: barryvdh
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+2 -3
View File
@@ -1,7 +1,6 @@
language: php
php:
- 5.6
- 7.0
- 7.1
- 7.2
@@ -17,11 +16,11 @@ cache:
matrix:
include:
- php: 5.6
- php: 7.0
env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest"'
before_script:
- travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist
- travis_wait 20 travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist
script:
- vendor/bin/phpcs --standard=psr2 src/
+10 -13
View File
@@ -11,23 +11,20 @@
],
"require": {
"php": ">=7",
"illuminate/support": "^5.5,<5.8",
"illuminate/console": "^5.5,<5.8",
"illuminate/filesystem": "^5.5,<5.8",
"barryvdh/reflection-docblock": "^2.0.4",
"composer/composer": "^1.6"
"illuminate/support": "^5.5|^6",
"illuminate/console": "^5.5|^6",
"illuminate/filesystem": "^5.5|^6",
"barryvdh/reflection-docblock": "^2.0.6",
"composer/composer": "^1.6",
"doctrine/dbal": "~2.3"
},
"require-dev": {
"illuminate/config": "^5.1,<5.8",
"illuminate/view": "^5.1,<5.8",
"illuminate/config": "^5.5|^6",
"illuminate/view": "^5.5|^6",
"phpro/grumphp": "^0.14",
"phpunit/phpunit" : "4.*",
"scrutinizer/ocular": "~1.1",
"squizlabs/php_codesniffer": "^3",
"doctrine/dbal": "~2.3"
},
"suggest": {
"doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)"
"squizlabs/php_codesniffer": "^3"
},
"autoload": {
"psr-4": {
@@ -46,7 +43,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
},
"laravel": {
"providers": [
+42 -14
View File
@@ -27,6 +27,18 @@ return array(
'include_fluent' => false,
/*
|--------------------------------------------------------------------------
| Factory Builders
|--------------------------------------------------------------------------
|
| Set to true to generate factory generators for better factory()
| method auto-completion.
|
*/
'include_factory_builders' => false,
/*
|--------------------------------------------------------------------------
| Write Model Magic methods
@@ -38,6 +50,21 @@ return array(
'write_model_magic_where' => true,
/*
|--------------------------------------------------------------------------
| Write Eloquent Model Mixins
|--------------------------------------------------------------------------
|
| This will add the necessary DocBlock mixins to the model class
| contained in the Laravel Framework. This helps the IDE with
| auto-completion.
|
| Please be aware that this setting changes a file within the /vendor directory.
|
*/
'write_eloquent_model_mixins' => false,
/*
|--------------------------------------------------------------------------
| Helper files to include
@@ -83,18 +110,7 @@ return array(
'Session' => array('Illuminate\Session\Store'),
),
'magic' => array(
'Log' => array(
'debug' => 'Monolog\Logger::addDebug',
'info' => 'Monolog\Logger::addInfo',
'notice' => 'Monolog\Logger::addNotice',
'warning' => 'Monolog\Logger::addWarning',
'error' => 'Monolog\Logger::addError',
'critical' => 'Monolog\Logger::addCritical',
'alert' => 'Monolog\Logger::addAlert',
'emergency' => 'Monolog\Logger::addEmergency',
)
),
'magic' => array(),
/*
|--------------------------------------------------------------------------
@@ -174,8 +190,20 @@ return array(
| Cast the given "real type" to the given "type".
|
*/
'type_overrides' => array(
'type_overrides' => array(
'integer' => 'int',
'boolean' => 'bool',
),
),
/*
|--------------------------------------------------------------------------
| Include DocBlocks from classes
|--------------------------------------------------------------------------
|
| Include DocBlocks from classes to allow additional code inspection for
| magic methods and properties.
|
*/
'include_class_docblocks' => false,
);
+29 -17
View File
@@ -1,4 +1,4 @@
## Laravel 5 IDE Helper Generator
## Laravel IDE Helper Generator
[![Latest Version on Packagist][ico-version]][link-packagist]
[![Software License][ico-license]](LICENSE.md)
@@ -7,6 +7,19 @@
__For Laravel 4.x, check [version 1.11](https://github.com/barryvdh/laravel-ide-helper/tree/1.11)__
* [Complete phpDocs, directly from the source](#complete-phpdocs--directly-from-the-source)
* [Install](#install)
* [Automatic phpDoc generation for Laravel Facades](#automatic-phpdoc-generation-for-laravel-facades)
* [Automatic phpDocs for models](#automatic-phpdocs-for-models)
* [Automatic phpDocs generation for Laravel Fluent methods](#automatic-phpdocs-generation-for-laravel-fluent-methods)
* [Auto-completion for factory builders](#auto-completion-for-factory-builders)
- [PhpStorm Meta for Container instances](#phpstorm-meta-for-container-instances)
- [Lumen Install](#lumen-install)
+ [Enabling Facades](#enabling-facades)
+ [Adding the Service Provider](#adding-the-service-provider)
+ [Adding Additional Facades](#adding-additional-facades)
* [License](#license)
### Complete phpDocs, directly from the source
_Check out [this Laracasts video](https://laracasts.com/series/how-to-be-awesome-in-phpstorm/episodes/15) for a quick introduction/explanation!_
@@ -29,7 +42,7 @@ Note: You do need CodeComplice for Sublime Text: https://github.com/spectacles/C
Require this package with composer using the following command:
```bash
composer require barryvdh/laravel-ide-helper
composer require --dev barryvdh/laravel-ide-helper
```
After updating composer, add the service provider to the `providers` array in `config/app.php`
@@ -39,12 +52,6 @@ Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
```
**Laravel 5.5** uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider.
To install this package on only development systems, add the `--dev` flag to your composer command:
```bash
composer require --dev barryvdh/laravel-ide-helper
```
In Laravel, instead of adding the service provider in the `config/app.php` file, you can add the following code to your `app/Providers/AppServiceProvider.php` file, within the `register()` method:
```php
@@ -75,8 +82,8 @@ You can configure your composer.json to do this after each commit:
"scripts":{
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta"
"@php artisan ide-helper:generate",
"@php artisan ide-helper:meta"
]
},
```
@@ -97,13 +104,6 @@ The `Illuminate/Support/helpers.php` is already set up, but you can add/remove y
### Automatic phpDocs for models
> You need to require `doctrine/dbal: ~2.3` in your own composer.json to get database columns.
```bash
composer require doctrine/dbal
```
If you don't want to write your properties yourself, you can use the command `php artisan ide-helper:models` to generate
phpDocs, based on table columns, relations and getters/setters. You can write the comments directly to your Model file, using the `--write (-W)` option. By default, you are asked to overwrite or write to a separate file (`_ide_helper_models.php`). You can force No with `--nowrite (-N)`.
Please make sure to backup your models, before writing the info.
@@ -169,6 +169,18 @@ After publishing vendor, simply change the `include_fluent` line your `config/id
And then run `php artisan ide-helper:generate` , you will now see all of the Fluent methods are recognized by your IDE.
### Auto-completion for factory builders
If you would like the `factory()->create()` and `factory()->make()` methods to return the correct model class,
you can enable custom factory builders with the `include_factory_builders` line your `config/ide-helper.php` file.
```php
'include_factory_builders' => true,
```
For this to work, you must also publish the PhpStorm Meta file (see below).
## PhpStorm Meta for Container instances
It's possible to generate a PhpStorm meta file to [add support for factory design pattern](https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata). For Laravel, this means we can make PhpStorm understand what kind of object we are resolving from the IoC Container. For example, `events` will return an `Illuminate\Events\Dispatcher` object, so with the meta file you can call `app('events')` and it will autocomplete the Dispatcher methods.
+26 -2
View File
@@ -1,4 +1,12 @@
<?= '<?php' ?>
<?php
/**
* @var \Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_alias_ns
* @var \Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_extends_ns
* @var bool $include_fluent
* @var string $helpers
*/
?>
// @formatter:off
@@ -28,7 +36,10 @@ namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
//Method inherited from <?= $method->getDeclaringClass() ?>
<?php endif; ?>
<?= $method->shouldReturn() ? 'return ': '' ?><?= $method->getRoot() ?>::<?= $method->getName() ?>(<?= $method->getParams() ?>);
<?php if($method->isInstanceCall()):?>
/** @var <?=$method->getRoot()?> $instance */
<?php endif?>
<?= $method->shouldReturn() ? 'return ': '' ?><?= $method->getRootMethodCall() ?>;
}
<?php endforeach; ?>
}
@@ -50,7 +61,10 @@ namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
//Method inherited from <?= $method->getDeclaringClass() ?>
<?php endif; ?>
<?= $method->shouldReturn() ? 'return ': '' ?><?= $method->getRoot() ?>::<?= $method->getName() ?>(<?= $method->getParams() ?>);
<?php if($method->isInstanceCall()):?>
/** @var <?=$method->getRoot()?> $instance */
<?php endif?>
<?= $method->shouldReturn() ? 'return ': '' ?><?= $method->getRootMethodCall() ?>;
}
<?php endforeach; ?>
<?php endif; ?>}
@@ -91,3 +105,13 @@ namespace Illuminate\Support {
class Fluent {}
}
<?php endif ?>
<?php foreach ($factories as $factory): ?>
namespace <?=$factory->getNamespaceName()?> {
/**
* @method \Illuminate\Database\Eloquent\Collection|<?=$factory->getShortName()?>[]|<?=$factory->getShortName()?> create($attributes = [])
* @method \Illuminate\Database\Eloquent\Collection|<?=$factory->getShortName()?>[]|<?=$factory->getShortName()?> make($attributes = [])
*/
class <?=$factory->getShortName()?>FactoryBuilder extends \Illuminate\Database\Eloquent\FactoryBuilder {}
}
<?php endforeach; ?>
+9
View File
@@ -20,6 +20,15 @@ namespace PHPSTORM_META {
]));
<?php endforeach; ?>
<?php if (count($factories)): ?>
override(\factory(0), map([
'' => '@FactoryBuilder',
<?php foreach($factories as $factory): ?>
'<?= $factory->getName() ?>' => \<?= $factory->getName() ?>FactoryBuilder::class,
<?php endforeach; ?>
]));
<?php endif; ?>
override(\Illuminate\Support\Arr::add(0), type(0));
override(\Illuminate\Support\Arr::except(0), type(0));
override(\Illuminate\Support\Arr::first(0), elementType(0));
+66 -17
View File
@@ -10,10 +10,13 @@
namespace Barryvdh\LaravelIdeHelper;
use Closure;
use ReflectionClass;
use Barryvdh\Reflection\DocBlock;
use Barryvdh\Reflection\DocBlock\Context;
use Barryvdh\Reflection\DocBlock\Tag\MethodTag;
use Illuminate\Config\Repository as ConfigRepository;
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
use ReflectionClass;
class Alias
{
@@ -34,17 +37,22 @@ class Alias
protected $interfaces = array();
protected $phpdoc = null;
/** @var ConfigRepository */
protected $config;
/**
* @param string $alias
* @param string $facade
* @param array $magicMethods
* @param array $interfaces
* @param ConfigRepository $config
* @param string $alias
* @param string $facade
* @param array $magicMethods
* @param array $interfaces
*/
public function __construct($alias, $facade, $magicMethods = array(), $interfaces = array())
public function __construct($config, $alias, $facade, $magicMethods = array(), $interfaces = array())
{
$this->alias = $alias;
$this->magicMethods = $magicMethods;
$this->interfaces = $interfaces;
$this->config = $config;
// Make the class absolute
$facade = '\\' . ltrim($facade, '\\');
@@ -171,10 +179,14 @@ class Alias
/**
* Get the methods found by this Alias
*
* @return array
* @return array|Method[]
*/
public function getMethods()
{
if (count($this->methods) > 0) {
return $this->methods;
}
$this->addMagicMethods();
$this->detectMethods();
return $this->methods;
@@ -304,13 +316,13 @@ class Alias
{
foreach ($this->magicMethods as $magic => $real) {
list($className, $name) = explode('::', $real);
if (!class_exists($className) && !interface_exists($className)) {
if ((!class_exists($className) && !interface_exists($className)) || !method_exists($className, $name)) {
continue;
}
$method = new \ReflectionMethod($className, $name);
$class = new \ReflectionClass($className);
if (!in_array($method->name, $this->usedMethods)) {
if (!in_array($magic, $this->usedMethods)) {
if ($class !== $this->root) {
$this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces);
}
@@ -356,14 +368,17 @@ class Alias
$properties = $reflection->getStaticProperties();
$macros = isset($properties['macros']) ? $properties['macros'] : [];
foreach ($macros as $macro_name => $macro_func) {
// Add macros
$this->methods[] = new Macro(
$this->getMacroFunction($macro_func),
$this->alias,
$reflection,
$macro_name,
$this->interfaces
);
if (!in_array($macro_name, $this->usedMethods)) {
// Add macros
$this->methods[] = new Macro(
$this->getMacroFunction($macro_func),
$this->alias,
$reflection,
$macro_name,
$this->interfaces
);
$this->usedMethods[] = $macro_name;
}
}
}
}
@@ -381,6 +396,10 @@ class Alias
return new \ReflectionMethod($macro_func[0], $macro_func[1]);
}
if (is_object($macro_func) && is_callable($macro_func) && !$macro_func instanceof Closure) {
return new \ReflectionMethod($macro_func, '__invoke');
}
return new \ReflectionFunction($macro_func);
}
@@ -395,12 +414,42 @@ class Alias
$serializer = new DocBlockSerializer(1, $prefix);
if ($this->phpdoc) {
if ($this->config->get('ide-helper.include_class_docblocks')) {
// if a class doesn't expose any DocBlock tags
// we can perform reflection on the class and
// add in the original class DocBlock
if (count($this->phpdoc->getTags()) === 0) {
$class = new ReflectionClass($this->root);
$this->phpdoc = new DocBlock($class->getDocComment());
}
}
$this->removeDuplicateMethodsFromPhpDoc();
return $serializer->getDocComment($this->phpdoc);
}
return '';
}
/**
* Removes method tags from the doc comment that already appear as functions inside the class.
* This prevents duplicate function errors in the IDE.
*
* @return void
*/
protected function removeDuplicateMethodsFromPhpDoc()
{
$methodNames = array_map(function (Method $method) {
return $method->getName();
}, $this->getMethods());
foreach ($this->phpdoc->getTags() as $tag) {
if ($tag instanceof MethodTag && in_array($tag->getMethodName(), $methodNames)) {
$this->phpdoc->deleteTag($tag);
}
}
}
/**
* Output an error.
*
+6 -1
View File
@@ -115,7 +115,10 @@ class GeneratorCommand extends Command
if ($written !== false) {
$this->info("A new helper file was written to $filename");
Eloquent::writeEloquentModelHelper($this, $this->files);
if ($this->option('write_mixins')) {
Eloquent::writeEloquentModelHelper($this, $this->files);
}
} else {
$this->error("The helper file could not be created at $filename");
}
@@ -159,9 +162,11 @@ class GeneratorCommand extends Command
protected function getOptions()
{
$format = $this->config->get('ide-helper.format');
$writeMixins = $this->config->get('ide-helper.write_eloquent_model_mixins');
return array(
array('format', "F", InputOption::VALUE_OPTIONAL, 'The format for the IDE Helper', $format),
array('write_mixins', "W", InputOption::VALUE_OPTIONAL, 'Write mixins to Laravel Model?', $writeMixins),
array('helpers', "H", InputOption::VALUE_NONE, 'Include the helper files'),
array('memory', "M", InputOption::VALUE_NONE, 'Use sqlite memory driver'),
array('sublime', "S", InputOption::VALUE_NONE, 'DEPRECATED: Use different style for SublimeText CodeIntel'),
+10 -3
View File
@@ -10,6 +10,7 @@
namespace Barryvdh\LaravelIdeHelper\Console;
use Barryvdh\LaravelIdeHelper\Factories;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -47,6 +48,7 @@ class MetaCommand extends Command
protected $methods = [
'new \Illuminate\Contracts\Container\Container',
'\Illuminate\Container\Container::makeWith(0)',
'\Illuminate\Contracts\Container\Container::make(0)',
'\Illuminate\Contracts\Container\Container::makeWith(0)',
'\App::make(0)',
@@ -76,6 +78,9 @@ class MetaCommand extends Command
*/
public function handle()
{
// Needs to run before exception handler is registered
$factories = $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [];
$this->registerClassAutoloadExceptions();
$bindings = array();
@@ -87,10 +92,11 @@ class MetaCommand extends Command
try {
$concrete = $this->laravel->make($abstract);
if (is_object($concrete)) {
$reflectionClass = new \ReflectionClass($concrete);
if (is_object($concrete) && !$reflectionClass->isAnonymous()) {
$bindings[$abstract] = get_class($concrete);
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
$this->comment("Cannot make '$abstract': ".$e->getMessage());
}
@@ -100,6 +106,7 @@ class MetaCommand extends Command
$content = $this->view->make('meta', [
'bindings' => $bindings,
'methods' => $this->methods,
'factories' => $factories,
])->render();
$filename = $this->option('filename');
@@ -135,7 +142,7 @@ class MetaCommand extends Command
protected function registerClassAutoloadExceptions()
{
spl_autoload_register(function ($class) {
throw new \Exception("Class '$class' not found.");
throw new \ReflectionException("Class '$class' not found.");
});
}
+40 -18
View File
@@ -96,7 +96,7 @@ class ModelsCommand extends Command
//If filename is default and Write is not specified, ask what to do
if (!$this->write && $filename === $this->filename && !$this->option('nowrite')) {
if ($this->confirm(
"Do you want to overwrite the existing model files? Choose no to write to $filename instead?"
"Do you want to overwrite the existing model files? Choose no to write to $filename instead"
)
) {
$this->write = true;
@@ -217,7 +217,7 @@ class ModelsCommand extends Command
$output .= $this->createPhpDocs($name);
$ignore[] = $name;
$this->nullableColumns = [];
} catch (\Exception $e) {
} catch (\Throwable $e) {
$this->error("Exception: " . $e->getMessage() . "\nCould not analyze class $name.");
}
}
@@ -290,7 +290,7 @@ class ModelsCommand extends Command
$realType = '\Illuminate\Support\Collection';
break;
default:
$realType = 'mixed';
$realType = class_exists($type) ? ('\\' . $type) : 'mixed';
break;
}
@@ -444,12 +444,27 @@ class ModelsCommand extends Command
array_shift($args);
$this->setMethod($name, '\Illuminate\Database\Eloquent\Builder|\\' . $reflection->class, $args);
}
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
$reflection = new \ReflectionClass($model);
$builder = get_class($model->newModelQuery());
$this->setMethod($method, "\\{$builder}|\\" . $reflection->getName());
} elseif (!method_exists('Illuminate\Database\Eloquent\Model', $method)
&& !Str::startsWith($method, 'get')
) {
//Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
$reflection = new \ReflectionMethod($model, $method);
if ($returnType = $reflection->getReturnType()) {
$type = $returnType instanceof \ReflectionNamedType
? $returnType->getName()
: (string)$returnType;
} else {
// php 7.x type or fallback to docblock
$type = (string)$this->getReturnTypeFromDocBlock($reflection);
}
$file = new \SplFileObject($reflection->getFileName());
$file->seek($reflection->getStartLine() - 1);
@@ -463,23 +478,24 @@ class ModelsCommand extends Command
$code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
foreach (array(
'hasMany',
'hasManyThrough',
'belongsToMany',
'hasOne',
'belongsTo',
'morphOne',
'morphTo',
'morphMany',
'morphToMany',
'morphedByMany'
) as $relation) {
'hasMany' => '\Illuminate\Database\Eloquent\Relations\HasMany',
'hasManyThrough' => '\Illuminate\Database\Eloquent\Relations\HasManyThrough',
'hasOneThrough' => '\Illuminate\Database\Eloquent\Relations\HasOneThrough',
'belongsToMany' => '\Illuminate\Database\Eloquent\Relations\BelongsToMany',
'hasOne' => '\Illuminate\Database\Eloquent\Relations\HasOne',
'belongsTo' => '\Illuminate\Database\Eloquent\Relations\BelongsTo',
'morphOne' => '\Illuminate\Database\Eloquent\Relations\MorphOne',
'morphTo' => '\Illuminate\Database\Eloquent\Relations\MorphTo',
'morphMany' => '\Illuminate\Database\Eloquent\Relations\MorphMany',
'morphToMany' => '\Illuminate\Database\Eloquent\Relations\MorphToMany',
'morphedByMany' => '\Illuminate\Database\Eloquent\Relations\MorphToMany'
) as $relation => $impl) {
$search = '$this->' . $relation . '(';
if ($pos = stripos($code, $search)) {
if (stripos($code, $search) || stripos($impl, (string)$type) !== false) {
//Resolve the relation's model to a Relation object.
$methodReflection = new \ReflectionMethod($model, $method);
if ($methodReflection->getNumberOfParameters()) {
return;
continue;
}
$relationObj = $model->$method();
@@ -495,7 +511,7 @@ class ModelsCommand extends Command
'morphToMany',
'morphedByMany',
];
if (in_array($relation, $relations)) {
if (strpos(get_class($relationObj), 'Many') !== false) {
//Collection or array of models (because Collection is Arrayable)
$this->setProperty(
$method,
@@ -503,6 +519,12 @@ class ModelsCommand extends Command
true,
null
);
$this->setProperty(
Str::snake($method) . '_count',
'int|null',
true,
false
);
} elseif ($relation === "morphTo") {
// Model isn't specified because relation is polymorphic
$this->setProperty(
@@ -716,7 +738,7 @@ class ModelsCommand extends Command
if (is_bool($default)) {
$default = $default ? 'true' : 'false';
} elseif (is_array($default)) {
$default = 'array()';
$default = '[]';
} elseif (is_null($default)) {
$default = 'null';
} elseif (is_int($default)) {
+37 -7
View File
@@ -32,27 +32,57 @@ class Eloquent
$reflection = new \ReflectionClass($class);
$namespace = $reflection->getNamespaceName();
$originalDoc = $reflection->getDocComment();
if (!$originalDoc) {
$command->info('Unexpected no document on ' . $class);
}
$phpdoc = new DocBlock($reflection, new Context($namespace));
$mixins = $phpdoc->getTagsByName('mixin');
foreach ($mixins as $m) {
if ($m->getContent() === '\Eloquent') {
$command->info('Tag Exists: @mixin \Eloquent in ' . $class);
$expectedMixins = [
'\Eloquent' => false,
'\Illuminate\Database\Eloquent\Builder' => false,
'\Illuminate\Database\Query\Builder' => false,
];
return;
foreach ($mixins as $m) {
$mixin = $m->getContent();
if (isset($expectedMixins[$mixin])) {
$command->info('Tag Exists: @mixin ' . $mixin . ' in ' . $class);
$expectedMixins[$mixin] = true;
}
}
// add the Eloquent mixin
$phpdoc->appendTag(Tag::createInstance("@mixin \\Eloquent", $phpdoc));
$changed = false;
foreach ($expectedMixins as $expectedMixin => $present) {
if ($present === false) {
$phpdoc->appendTag(Tag::createInstance('@mixin ' . $expectedMixin, $phpdoc));
$changed = true;
}
}
// If nothing's changed, stop here.
if (!$changed) {
return;
}
$serializer = new DocBlockSerializer();
$serializer->getDocComment($phpdoc);
$docComment = $serializer->getDocComment($phpdoc);
/*
The new DocBlock is appended to the beginning of the class declaration.
Since there is no DocBlock, the declaration is used as a guide.
*/
if (!$originalDoc) {
$originalDoc = 'abstract class Model implements';
$docComment .= "\nabstract class Model implements";
}
$filename = $reflection->getFileName();
if ($filename) {
$contents = $files->get($filename);
@@ -61,7 +91,7 @@ class Eloquent
$contents = str_replace($originalDoc, $docComment, $contents, $count);
if ($count > 0) {
if ($files->put($filename, $contents)) {
$command->info('Wrote @mixin \Eloquent to ' . $filename);
$command->info('Wrote expected docblock to ' . $filename);
} else {
$command->error('File write failed to ' . $filename);
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Barryvdh\LaravelIdeHelper;
use Exception;
use Illuminate\Database\Eloquent\Factory;
use ReflectionClass;
class Factories
{
public static function all()
{
$factories = [];
$factory = app(Factory::class);
$definitions = (new ReflectionClass(Factory::class))->getProperty('definitions');
$definitions->setAccessible(true);
foreach ($definitions->getValue($factory) as $factory_target => $config) {
try {
$factories[] = new ReflectionClass($factory_target);
} catch (Exception $exception) {
}
}
return $factories;
}
}
+10 -5
View File
@@ -86,6 +86,7 @@ class Generator
->with('helpers', $this->helpers)
->with('version', $app->version())
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
->with('factories', $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [])
->render();
}
@@ -198,12 +199,12 @@ class Generator
// Get all aliases
foreach ($this->getAliases() as $name => $facade) {
// Skip the Redis facade, if not available (otherwise Fatal PHP Error)
if ($facade == 'Illuminate\Support\Facades\Redis' && !class_exists('Predis\Client')) {
if ($facade == 'Illuminate\Support\Facades\Redis' && $name == 'Redis' && !class_exists('Predis\Client')) {
continue;
}
$magicMethods = array_key_exists($name, $this->magic) ? $this->magic[$name] : array();
$alias = new Alias($name, $facade, $magicMethods, $this->interfaces);
$alias = new Alias($this->config, $name, $facade, $magicMethods, $this->interfaces);
if ($alias->isValid()) {
//Add extra methods, from other classes (magic static calls)
if (array_key_exists($name, $this->extra)) {
@@ -272,9 +273,13 @@ class Generator
$facades = array_merge($facades, $this->config->get('app.aliases', []));
// Only return the ones that actually exist
return array_filter($facades, function ($alias) {
return class_exists($alias);
});
return array_filter(
$facades,
function ($alias) {
return class_exists($alias);
},
ARRAY_FILTER_USE_KEY
);
}
/**
+4 -2
View File
@@ -37,8 +37,10 @@ class IdeHelperServiceProvider extends ServiceProvider
*/
public function boot()
{
$viewPath = __DIR__.'/../resources/views';
$this->loadViewsFrom($viewPath, 'ide-helper');
if ($this->app->has('view')) {
$viewPath = __DIR__ . '/../resources/views';
$this->loadViewsFrom($viewPath, 'ide-helper');
}
$configPath = __DIR__ . '/../config/ide-helper.php';
if (function_exists('config_path')) {
+16 -22
View File
@@ -17,36 +17,30 @@ class Macro extends Method
* @param array $interfaces
*/
public function __construct(
\ReflectionFunctionAbstract $method,
$method,
$alias,
$class,
$methodName = null,
$interfaces = array()
) {
$this->method = $method;
$this->interfaces = $interfaces;
$this->name = $methodName ?: $method->name;
$this->namespace = $class->getNamespaceName();
parent::__construct($method, $alias, $class, $methodName, $interfaces);
}
//Create a DocBlock and serializer instance
/**
* @param \ReflectionFunctionAbstract $method
*/
protected function initPhpDoc($method)
{
$this->phpdoc = new DocBlock($method);
}
//Normalize the description and inherit the docs from parents/interfaces
try {
$this->normalizeParams($this->phpdoc);
$this->normalizeReturn($this->phpdoc);
$this->normalizeDescription($this->phpdoc);
} catch (\Exception $e) {
}
//Get the parameters, including formatted default values
$this->getParameters($method);
//Make the method static
$this->phpdoc->appendTag(Tag::createInstance('@static', $this->phpdoc));
//Reference the 'real' function in the declaringclass
/**
* @param \ReflectionFunctionAbstract $method
* @param \ReflectionClass $class
*/
protected function initClassDefinedProperties($method, \ReflectionClass $class)
{
$this->namespace = $class->getNamespaceName();
$this->declaringClassName = '\\' . ltrim($class->name, '\\');
$this->root = '\\' . ltrim($class->getName(), '\\');
}
}
+62 -7
View File
@@ -26,29 +26,36 @@ class Method
protected $method;
protected $output = '';
protected $declaringClassName;
protected $name;
protected $namespace;
protected $params = array();
protected $params_with_default = array();
protected $interfaces = array();
protected $real_name;
protected $return = null;
protected $root;
/**
* @param \ReflectionMethod $method
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
* @param string $alias
* @param \ReflectionClass $class
* @param string|null $methodName
* @param array $interfaces
*/
public function __construct(\ReflectionMethod $method, $alias, $class, $methodName = null, $interfaces = array())
public function __construct($method, $alias, $class, $methodName = null, $interfaces = array())
{
$this->method = $method;
$this->interfaces = $interfaces;
$this->name = $methodName ?: $method->name;
$this->namespace = $method->getDeclaringClass()->getNamespaceName();
$this->real_name = $method->isClosure() ? $this->name : $method->name;
$this->initClassDefinedProperties($method, $class);
//Reference the 'real' function in the declaring class
$this->root = '\\' . ltrim($class->getName(), '\\');
//Create a DocBlock and serializer instance
$this->phpdoc = new DocBlock($method, new Context($this->namespace));
$this->initPhpDoc($method);
//Normalize the description and inherit the docs from parents/interfaces
try {
@@ -63,11 +70,25 @@ class Method
//Make the method static
$this->phpdoc->appendTag(Tag::createInstance('@static', $this->phpdoc));
}
//Reference the 'real' function in the declaringclass
/**
* @param \ReflectionMethod $method
*/
protected function initPhpDoc($method)
{
$this->phpdoc = new DocBlock($method, new Context($this->namespace));
}
/**
* @param \ReflectionMethod $method
* @param \ReflectionClass $class
*/
protected function initClassDefinedProperties($method, \ReflectionClass $class)
{
$declaringClass = $method->getDeclaringClass();
$this->namespace = $declaringClass->getNamespaceName();
$this->declaringClassName = '\\' . ltrim($declaringClass->name, '\\');
$this->root = '\\' . ltrim($class->getName(), '\\');
}
/**
@@ -90,6 +111,26 @@ class Method
return $this->root;
}
/**
* @return bool
*/
public function isInstanceCall()
{
return ! ($this->method->isClosure() || $this->method->isStatic());
}
/**
* @return string
*/
public function getRootMethodCall()
{
if ($this->isInstanceCall()) {
return "\$instance->{$this->getRealName()}({$this->getParams()})";
} else {
return "{$this->getRoot()}::{$this->getRealName()}({$this->getParams()})";
}
}
/**
* Get the docblock for this method
*
@@ -112,6 +153,16 @@ class Method
return $this->name;
}
/**
* Get the real method name
*
* @return string
*/
public function getRealName()
{
return $this->real_name;
}
/**
* Get the parameters for this method
*
@@ -213,6 +264,10 @@ class Method
// Set the changed content
$tag->setContent($returnValue . ' ' . $tag->getDescription());
$this->return = $returnValue;
if ($tag->getType() === '$this') {
$tag->setType($this->root);
}
} else {
$this->return = null;
}
@@ -266,7 +321,7 @@ class Method
if (is_bool($default)) {
$default = $default ? 'true' : 'false';
} elseif (is_array($default)) {
$default = 'array()';
$default = '[]';
} elseif (is_null($default)) {
$default = 'null';
} elseif (is_int($default)) {