Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions resources/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,8 @@ label.required:before {
.text-right {
text-align: right;
}

.anchor-link {
margin-left: .1em;
padding: .1em .3em;
}
96 changes: 6 additions & 90 deletions resources/docs/integration-to-new-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,97 +93,13 @@ Again, we run Rector, apply changes, create pull-request, and go for merge. Slow

<br>

Every codebase is different, and sometimes, we come across a rule that is not safe to apply. We can skip it for now:
### Skip what you don't need

```diff
<?php

use Rector\Config\RectorConfig;
+use Rector\Php54\Rector\FuncCall\RemoveReferenceFromCallRector;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
+ ->withSkip([RemoveReferenceFromCallRector::class])
->withPhpSets(php54: true);
```

This is an entirely valid upgrade process. We will deal with skips later once our codebase is upgraded to our PHP version and much more robust.

Check [other ways to use `withSkip()`](/documentation/ignoring-rules-or-paths).

## 4. Type Coverage Level

What is a PHP 8.3 project without a single type declaration?

A horse with Tesla bodywork.

<br>

Type coverage is one of the most influential metrics in a modern PHP project. We can have a high PHP version in `composer.json`, but our code can still be full of `mixed` types, giving us zero confidence. There is a PHPStan package - [`type-coverage`](https://github.com/TomasVotruba/type-coverage)- that helps raise the bar 1% at a time.

How can we use Rector to help out with type coverage? We can add a prepared set:

```diff
<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
+ ->withPreparedSets(typeDeclarations: true);
```

Let's run Rector:

```bash
vendor/bin/rector --dry-run
```

Wow, over 90 % of files were changed. That's going to be a very long review. We can do better than that.

## 5. One Level at a Time

Instead of applying ~50 type declaration rules at once, we can apply them individually. This is much easier to review and explain to your team. But which one should we start with?

We took the liberty of sorting the rules from the easy-pick to more complex ones. You can enable them yourself and go one level at a time:

```diff
<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
- ->withPreparedSets(typeDeclaration: true);
+ ->withTypeCoverageLevel(1);
```

Now run Rector to see the changed files:

```php
vendor/bin/rector
```

Only five files? We can do that in a day. We create a pull request, get a review, and merge. The next day, we can continue with level 2. You get the idea.

## 6. Dead Code Level

Are you done with the type level and reached [99 % type coverage](https://github.com/tomasVotruba/type-coverage)? It's time to move on to dead code removal.

Again, we could use the prepared dead-code set, but the number of changes would be huge. Instead, we make use of the dead-code level:

```php
<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
->withTypeCoverageLevel(40)
->withDeadCodeLevel(1);
```
Every codebase is different, and sometimes, we come across a rule that is not safe to apply, or files we want to skip.
Use [ignoring](/documentation/ignoring-rules-or-paths) for that.

## Prepared sets, one level at a time

We increase it by 1, run Rector, create a pull request, get a review, and merge.
Rector provides dozens of [prepared sets](/documentation/set-lists). But the same way we don't read every book in library we visit for first time, we don't enable all prepared sets at once.

Once we reach the highest dead code level, we can move on to [next prepared sets](/documentation/set-lists).
Instead, **use [level methods](/documentation/levels) and take it step by step**. It more relaxing path to reach the goal.
76 changes: 76 additions & 0 deletions resources/docs/levels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
What is a PHP 8.3 project without a single type declaration?

A horse with Tesla bodywork.

<br>

Type coverage is one of the most influential metrics in a modern PHP project. We can have a high PHP version in `composer.json`, but our code can still be full of `mixed` types, giving us zero confidence. There is a [`type-coverage`](https://github.com/TomasVotruba/type-coverage) package that helps raise the bar 1 % at a time.

How can we use Rector to help out with type coverage?

**What happens if we add full set:**

```diff
<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
+ ->withPreparedSets(typeDeclarations: true);
```

Let's run Rector:

```bash
vendor/bin/rector --dry-run
```

Wow, over **90 % of files have changed**. That's going to be a very long review. We can do better than that.

## One Level at a Time

Instead of applying ~50 type declaration rules at once, we can apply them individually. This is much easier to review and explain to your team. But which one should we start with?

We took the liberty of sorting the rules from the easy-pick to more complex ones. You can enable them yourself and go one level at a time:

```diff
<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
- ->withPreparedSets(typeDeclaration: true);
+ ->withTypeCoverageLevel(1);
```

Now run Rector to see the changed files:

```php
vendor/bin/rector
```

Only five files? We can do that in a day. We create a pull request, get a review, and merge. The next day, we can continue with level 2. You get the idea.

## Dead Code and Code Quality Level

Are you done with the type level and reached [99 % type coverage](https://github.com/tomasVotruba/type-coverage)? It's time to move on to dead code removal and improve code quality.

Again, we avoid full-blown prepared set, and make use of level methods:

```php
<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
->withTypeCoverageLevel(1)
->withDeadCodeLevel(1)
->withCodeQualityLevel(1);
```

We increase it by 1, run Rector, create a pull request, get a review, and merge.

Once we reach the highest level, we can move on to [next prepared sets](/documentation/set-lists#content-prepared-sets).
15 changes: 14 additions & 1 deletion resources/docs/set-lists.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## Prepared Sets

You can use particular single rules, or whole list of rules, called "set lists":

```php
Expand All @@ -6,11 +8,22 @@ You can use particular single rules, or whole list of rules, called "set lists":
use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPreparedSets(deadCode: true, codeQuality: true);
->withPreparedSets(
deadCode: true,
codeQuality: true,
codingStyle: true,
naming: true,
privatization: true,
typeDeclarations: true,
rectorPreset: true,
// ...
);
```

That way you can include group of rules that focus on certain topic, e.g. in this case on dead detection. It makes config small and clear.

Try autocomplete in your IDE to see all available prepared sets.

## PHP Sets

The best practise is to use PHP version defined in `composer.json`. Rector will automatically pick it up with empty `->withPhpSets()` method:
Expand Down
1 change: 1 addition & 0 deletions src/Documentation/DocumentationMenuFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function create(): array
$this->documentationMenuItemFactory->createSection('integration-to-new-project', 'New Project', true),
$this->documentationMenuItemFactory->createSection('define-paths', 'Define Paths'),
$this->documentationMenuItemFactory->createSection('set-lists', 'Set Lists'),
$this->documentationMenuItemFactory->createSection('levels', 'Levels', true),
$this->documentationMenuItemFactory->createSection('composer-based-sets', 'Composer-Based Sets', true),
$this->documentationMenuItemFactory->createInternalLink(FindRuleController::class, 'Find Rules'),
$this->documentationMenuItemFactory->createSection(
Expand Down
33 changes: 33 additions & 0 deletions src/Markdown/GithubMarkdownConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace App\Markdown;

use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
use League\CommonMark\MarkdownConverter;

/**
* Same as \League\CommonMark\GithubFlavoredMarkdownConverter
* just with HeadingPermalinkExtension
*/
final class GithubMarkdownConverter extends MarkdownConverter
{
/**
* @param array<string, mixed> $config
*/
public function __construct(array $config = [])
{
$environment = new Environment($config);
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new GithubFlavoredMarkdownExtension());

// @see https://commonmark.thephpleague.com/1.5/extensions/heading-permalinks/
$environment->addExtension(new HeadingPermalinkExtension());

parent::__construct($environment);
}
}
10 changes: 7 additions & 3 deletions src/Thumbnail/ThumbnailGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ public function addRectorLogo(ImageInterface $image): void
{
$rectorLogoImage = $this->imagine->open(__DIR__ . '/../../public/assets/images/new-logo/rector-square.png');

$currentWidth = $rectorLogoImage->getSize()->getWidth();
$currentHeight = $rectorLogoImage->getSize()->getHeight();
$currentWidth = $rectorLogoImage->getSize()
->getWidth();
$currentHeight = $rectorLogoImage->getSize()
->getHeight();

$rectorLogoImage->resize(new Box($currentWidth * self::RESIZE_LOGO_RATIO, $currentHeight * self::RESIZE_LOGO_RATIO));
$rectorLogoImage->resize(
new Box($currentWidth * self::RESIZE_LOGO_RATIO, $currentHeight * self::RESIZE_LOGO_RATIO)
);

$image->paste($rectorLogoImage, new Point(1450, 50));
}
Expand Down
11 changes: 9 additions & 2 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// @see https://github.com/thephpleague/commonmark

use App\Enum\FlashType;
use App\Markdown\GithubMarkdownConverter;
use Illuminate\Http\RedirectResponse;
use League\CommonMark\GithubFlavoredMarkdownConverter;

function latestPhp(): string
{
Expand All @@ -21,8 +21,15 @@ function slugify(string $value): string

function markdown(string $contents): Stringable
{
$markdownConverter = new GithubFlavoredMarkdownConverter([
// @see https://commonmark.thephpleague.com/1.5/extensions/heading-permalinks/
$markdownConverter = new GithubMarkdownConverter([
'allow_unsafe_links' => false,
'heading_permalink' => [
'html_class' => 'anchor-link', // CSS class for the anchor
'symbol' => '#', // Symbol for the link
'id_prefix' => 'content', // Prefix for the ID
'insert' => 'after', // Where to insert the link: 'before', 'after', or 'none'
],
]);

return $markdownConverter->convert($contents);
Expand Down
Loading