Skip to content

Commit

Permalink
added docs for "fix-annotations-in-extended-classes" phing target
Browse files Browse the repository at this point in the history
  • Loading branch information
vitek-rostislav committed Aug 23, 2019
1 parent 6dcb547 commit a299fe1
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
Expand Up @@ -229,6 +229,11 @@ Lists all available background jobs. If there is more than one cron instance reg

For more information, see [Working with Multiple Cron Instances](/docs/cookbook/working-with-multiple-cron-instances.md) cookbook or you can read about [Cron in general](/docs/introduction/cron.md).

#### fix-annotations-in-extended-classes
Makes static analysis tools understand the extended code in your project by changing annotations and adding `@property` and `@method` annotations to relevant classes-

You can read more in the ["Framework extensibility" article](./framework-extensibility.md#making-the-static-analysis-understand-the-extended-code).

#### grunt
Builds CSS from LESS via Grunt.

Expand Down
5 changes: 5 additions & 0 deletions docs/introduction/faq-and-common-issues.md
Expand Up @@ -26,6 +26,7 @@ For more detailed information about the Shopsys Framework, please see [Shopsys F
- [How can I create Front-end Breadcrumb navigation?](#how-can-i-create-front-end-breadcrumb-navigation)
- [Do you have any tips how to debug emails during development in Docker?](#do-you-have-any-tips-how-to-debug-emails-during-development-in-docker)
- [Can I see what is really happening in the Codeception acceptance tests when using Docker?](#can-i-see-what-is-really-happening-in-the-codeception-acceptance-tests-when-using-docker)
- [How to make PHPStorm and PHPStan understand that I use extended classes?]()

## What are the phing targets?
Every phing target is a task that can be executed simply by `php phing <target-name>` command.
Expand Down Expand Up @@ -148,3 +149,7 @@ See [Outgoing emails](https://github.com/djfarrelly/MailDev#outgoing-email) in t

## Can I see what is really happening in the Codeception acceptance tests when using Docker?
Yes, you can! Check [the quick guide](/docs/introduction/running-acceptance-tests.md#how-to-watch-what-is-going-on-in-the-selenium-browser).

## How to make PHPStorm and PHPStan understand that I use extended classes?
There is a phing target that automatically fixes all relevant `@var` and `@param` annotations, and adds proper `@method` and `@property` annotations to your classes so the static analysis understands the class extensions properly.
You can read more in the ["Framework extensibility" article](./framework-extensibility.md#making-the-static-analysis-understand-the-extended-code).
128 changes: 128 additions & 0 deletions docs/introduction/framework-extensibility.md
Expand Up @@ -78,3 +78,131 @@ as well as a list of customizations that are not (and will not be) possible at a
* separate users login credentials
* share company attributes
* change association from 1:1 to 1:N

## Making the static analysis understand the extended code
### Problem description
When extending framework classes, it may happen that tools for static analysis (e.g. PHPStan, PHPStorm) will not understand your code properly.
Imagine this situation:
- You have a controller that is dependent on a framework service:
```php
namespace Shopsys\ShopBundle\Controller\Front;

use Shopsys\FrameworkBundle\Model\Product\ProductFacade;

class ProductController
{
/**
* @var \Shopsys\FrameworkBundle\Model\Product\ProductFacade
*/
protected $productFacade;

/**
* @param \Shopsys\FrameworkBundle\Model\Product\ProductFacade $productFacade
*/
public function __construct(ProductFacade $productFacade)
{
$this->productFacade = $productFacade;
}
}
```
- In your project, you extend the framework service:
```php
namespace Shopsys\ShopBundle\Model\Product;

use Shopsys\FrameworkBundle\Model\Product\ProductFacade as BaseProductFacade;

class ProductFacade extends BaseProductFacade
{
public function myCustomAwesomeFunction()
{
return 42;
}
}
```
- You register your extension in DI services configuration and thanks to that, your class is used `ProductController` instead of the one from `FrameworkBundle`, so far so good:
```yaml
Shopsys\FrameworkBundle\Model\Product\ProductFacade: '@Shopsys\ShopBundle\Model\Product\ProductFacade'
```
**However, when you want to use your `myCustomAwesomeFunction()` in `ProductController`, the static analysis is not aware of that function.**
#### Solution
To fix this, you need to change the annotations properly:
```diff
class ProductController
{
/**
- * @var \Shopsys\FrameworkBundle\Model\Product\ProductFacade
+ * @var \Shopsys\FrameworkBundle\Model\Product\ProductFacade
*/
protected $productFacade;

/**
- * @param \Shopsys\FrameworkBundle\Model\Product\ProductFacade $productFacade
+ * @param \Shopsys\FrameworkBundle\Model\Product\ProductFacade $productFacade
*/
public function __construct(ProductFacade $productFacade)
{
$this->productFacade = $productFacade;
}
}
```
**Luckily, you do need to fix the annotations manually, there is the `fix-annotations-in-extended-classes` phing command, that handles everything for you.**

### Another problem description
There might be yet another problem with static analysis when extending framework classes.
Imagine the following situation:
- In framework, there is `ProductFacade` that has `ProductRepository` property
```php
namespace Shopsys\FrameworkBundle\Model\Product;

class ProductFacade
{
/**
* @var \Shopsys\FrameworkBundle\Model\Product\ProductRepository
*/
protected $productRepository;

/**
* @return \Shopsys\FrameworkBundle\Model\Product\ProductRepository
*/
public function getProductRepository()
{
retrun $this->productRepository;
}
}
```
- In your project, you extend `ProductRepository` and `ProductFacade` as well.
- Then, in your extended facade, you want to access the repository (generally speaking, you want to access the parent's property that has a type that is extended in your project, or you want to access a method that returns a type that is already extended):
```php
namespace Shopsys\ShopBundle\Model\Product;

use Shopsys\FrameworkBundle\Model\Product\ProductFacade as BaseProductFacade;

class ProductFacade extends BaseProductFacade
{
public function myCustomAwesomeFunction()
{
$this->productRepository; // static analysis thinks this is of type \Shopsys\FrameworkBundle\Model\Product\ProductRepository
$this->>getProductRepository(); // static analysis thinks this is of type \Shopsys\FrameworkBundle\Model\Product\ProductRepository
}
}
```
- **Once again, static analysis is not aware of the extension.**
#### Solution
To fix this, you do need to override the method or property, you just need to add proper `@method` and `@property` annotations to your class:
```diff
namespace Shopsys\ShopBundle\Model\Product;

use Shopsys\FrameworkBundle\Model\Product\ProductFacade as BaseProductFacade;

+ /**
+ * @method \Shopsys\ShopBundle\Model\Product\ProductRepository getProductRepository()
+ * @property \Shopsys\ShopBundle\Model\Product\ProductRepository $productRepository
+ */
class ProductFacade extends BaseProductFacade
{
```
**Even this scenario is covered by `fix-annotations-in-extended-classes` phing command.**

### Tip
If you are a fan an automation and PHPStorm user at the same time, you can simplify things even more a set your IDE to automatically run the phing target every time you e.g. change something in your project.
This can be achieved by setting up a custom "[File watcher](https://www.jetbrains.com/help/phpstorm/using-file-watchers.html)".

0 comments on commit a299fe1

Please sign in to comment.