From 97ae2dfc605a801b52cb3dbef04b58e52b296577 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 29 Aug 2024 18:08:55 +0200 Subject: [PATCH] [deprecations] Deprecating few lightyears irelevant/non existing topic posts, to make other more visible (#1478) * deprecate inject post * deprecate extension post * deprecate non existing ecs config post * deprecate apigen post * deprecate legacy version + non existing page * deprecate ecs nonexisting config post * deprecate confusing config post * deprecate very narrow easy admin post * deprecate package builder post - non existing * deprecate non existing sniff * deprecate non existing nor relevant topics * deprecate non relevant --- ...ct-thanks-to-decorator-feature-in-nette.md | 259 ---------- ...ay-to-create-your-first-nette-extension.md | 180 ------- ...new-features-in-easy-coding-standard-22.md | 125 ----- ...09-04-how-apigen-survived-its-own-death.md | 207 -------- ...7-united-php-71-adoption-6-months-later.md | 126 ----- ...symfony-standard-with-yaml-and-services.md | 125 ----- ...-to-add-coding-standards-to-big-project.md | 130 ----- ...tin-in-easy-admin-bundle-with-collector.md | 322 ------------- ...-3-new-cool-features-of-package-builder.md | 199 -------- ...r-and-external-final-in-coding-standard.md | 78 --- ...merge-and-split-monorepo-with-1-command.md | 101 ---- ...hout-bundle-extension-and-configuration.md | 379 --------------- ...-steps-to-migrate-from-jekyll-to-statie.md | 140 ------ ...elease-from-2-days-to-1-console-command.md | 161 ------- ...-of-php-packages-psalm-fixing-your-code.md | 193 -------- ...pkari-cz-from-symfony-4-to-5-in-25-days.md | 445 ------------------ ...ny-app-to-phar-without-killing-yourself.md | 188 -------- 17 files changed, 3358 deletions(-) delete mode 100644 resources/posts/2016/2016-12-24-how-to-avoid-inject-thanks-to-decorator-feature-in-nette.md delete mode 100644 resources/posts/2017/2017-02-15-minimalistic-way-to-create-your-first-nette-extension.md delete mode 100644 resources/posts/2017/2017-08-07-7-new-features-in-easy-coding-standard-22.md delete mode 100644 resources/posts/2017/2017-09-04-how-apigen-survived-its-own-death.md delete mode 100644 resources/posts/2017/2017-11-27-united-php-71-adoption-6-months-later.md delete mode 100644 resources/posts/2018/2018-03-26-new-in-easy-coding-standard-4-clean-symfony-standard-with-yaml-and-services.md delete mode 100644 resources/posts/2018/2018-05-24-boss-vs-masseuse-way-to-add-coding-standards-to-big-project.md delete mode 100644 resources/posts/2018/2018-08-20-painful-experience-over-solutions-extend-configuratin-in-easy-admin-bundle-with-collector.md delete mode 100644 resources/posts/2018/2018-09-20-new-in-symplify-5-3-new-cool-features-of-package-builder.md delete mode 100644 resources/posts/2018/2018-10-04-new-in-symplify-5-public-method-order-and-external-final-in-coding-standard.md delete mode 100644 resources/posts/2018/2018-10-08-new-in-symplify-5-create-merge-and-split-monorepo-with-1-command.md delete mode 100644 resources/posts/2018/2018-11-29-how-to-manage-configuration-in-symfony-without-bundle-extension-and-configuration.md delete mode 100644 resources/posts/2019/2019-01-10-9-steps-to-migrate-from-jekyll-to-statie.md delete mode 100644 resources/posts/2019/2019-02-18-how-we-automated-shopsys-packages-release-from-2-days-to-1-console-command.md delete mode 100644 resources/posts/2019/2019-05-13-hidden-gems-of-php-packages-psalm-fixing-your-code.md delete mode 100644 resources/posts/2019/2019-09-09-how-we-upgraded-pehapkari-cz-from-symfony-4-to-5-in-25-days.md delete mode 100644 resources/posts/2019/2019-12-02-how-to-box-symfony-app-to-phar-without-killing-yourself.md diff --git a/resources/posts/2016/2016-12-24-how-to-avoid-inject-thanks-to-decorator-feature-in-nette.md b/resources/posts/2016/2016-12-24-how-to-avoid-inject-thanks-to-decorator-feature-in-nette.md deleted file mode 100644 index cfd6dda1a8f..00000000000 --- a/resources/posts/2016/2016-12-24-how-to-avoid-inject-thanks-to-decorator-feature-in-nette.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -id: 19 -title: "How to avoid @inject thanks to Decorator feature in Nette" -perex: | - I often find `@inject` being overused in projects I review while mentoring. They often bring less writing, but in exchange they break [SOLID principles](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)). - - - Today I will show you solution that will **keep your code both small and clean** - **Decorator feature in Nette**. - ---- - -As [Derek Simons says](https://www.ted.com/talks/simon_sinek_how_great_leaders_inspire_action) says... - -## ...Start with "Why" - -Why am I writing this article? I try to improve knowledge interoperability between frameworks so it **is easier to understand and use each other**. The goal is to discourage Nette- (or any framework-) specific things **in favor of those that may be common**. - -Today, I will try to agree on setter injection with you. - - -## `@Inject` Overuse is Spreading - -This code is common to 80 % Nette applications I came across in last year: - -```php -// app/Presenter/ProductPresenter.php - -namespace App\Presenter; - -final class ProductPresenter extends AbstractBasePresenter -{ - /** - * @inject - * @var ProductRepository - */ - public $productRepository; -} -``` - -Using `@inject` annotations over constructor injection is **fast, short and it just works**. - -Ok, why not use it everywhere: - -```php -// app/Repository/ProductRepository.php - -namespace App\Repository; - -class ProductRepository -{ - /** - * @inject - * @var Doctrine - */ - public $entityManager; -} -``` - -and - -```yaml -# app/config/config.neon - -services: - - - class: App\Repository\ProductRepository - inject: on -``` - -## Your Code is Seen as Manual How to Write - -Why? Because "what you see is what you write". New programmer joins the teams, sees this handy `@inject` feature and uses when possible and handy. - -Some of you, who already talked about `@inject` method usage already there are some and only few specific places to use it. - -## Where to only `@inject`? - -**To prevent constructor hell**. If you meet this term first time, go read [this short explanation](https://phpfashion.com/di-a-predavani-zavislosti#toc-constructor-hell) by David Grudl. - -The best use case is `AbstractBasePresenter`. -Let's say I need `Translator` service in all of my presenters. - -```php -// app/Presenter/AbstractBasePresenter.php - -namespace App\Presenter; - -abstract class AbstractBasePresenter extends Nette\Application\UI\Presenter -{ - /** - * @inject - * @var Translator - */ - public $translator; -} -``` - -And I can use it in `ProductPresenter` along with constructor injection - -```php -// app/Presenter/ProductPresenter.php - -namespace App\Presenter; - -final class ProductPresenter extends AbstractBasePresenter -{ - /** - * @var ProductRepository - */ - private $productRepository; - - public function __construct(ProductRepository $productRepository) - { - $this->productRepository = $productRepository; - } -} -``` - -This is quite clean and easy to use, because presenters have injects [enabled by default](https://github.com/nette/application/blob/3165d3a8dab876f4364cdcba450a33ab0182049a/src/Bridges/ApplicationDI/ApplicationExtension.php#L111-L116). - - -## Level up - -But what if we have other objects that: - - - **inherit from abstract parent** - - needs **1 service available everywhere** - -2 common case pop to my mind: - -- `AbstractBaseRepository` for all our repositories -- `AbstractBaseControl` for all our components - -Let's take the first one: - -```php -// app/Repository/AbstractBaseRepository.php - -namespace App\Repository; - -use Doctrine\ORM\EntityManagerInterface; - -abstract class AbstractBaseRepository -{ - /** - * @var EntityManagerInterface - */ - protected $entityManager; - - public function setEntityManager(EntityManagerInterface $entityManager) - { - $this->entityManager = $entityManager; - } -} -``` - -And specific repository with some dependency: - -```php -// app/Repository/ProductRepository.php - -namespace App\Repository; - -use App\Model\Product\ProductSorter; - -final ProductRepository extends AbstractBaseRepository -{ - /** - * @var ProductSorter - */ - private $productSorter; - - public function __construct(ProductSorter $productSorter) - { - $this->productSorter = $productSorter; - } -} -``` - -So our config would look like: - -```yaml -# app/config/config.neon - -services: - - - class: App\Repository\ProductRepository - setup: - - setEntityManager - # and for other repositories - - - class: App\Repository\UserRepository - setup: - - setEntityManager - - - class: App\Repository\CategoryRepository - setup: - - setEntityManager -``` - -### SO much writing! - -It is cleaner, but with so much writing? Thanks, but no, thanks. Let's go back to `@inject`... - -Wait! Before any premature conclusion, let's set the goal first. - -### What is Desired Result? - -```yaml -# app/config.config.neon - -services: - - App\Repository\ProductRepository - - App\Repository\UserRepository - - App\Repository\CategoryRepository -``` - -That would be great, right? Is that possible in Nette while keeping the code clean? - -## Decorator Extension to the Rescue - -This feature is in Nette [since 2014](https://github.com/nette/di/commit/28fdac304b967ae43a90936069d94316ee2daca4) (<= the best documentation for it so far). - -How does it work? - -```yaml -# app/config/config.neon - -decorator: # keyword used by Nette - App\Repository\AbstractBaseRepository: # 1. find every service this type - setup: # same setup as we use in service configuration - - setEntityManager # 2. call this setter injection on it - - # or do you need to call "setTranslator" on every component? - App\Component\AbstractBaseControl: - setup: - - setTranslator -``` - -That's it! - - -## What Have You Learned Today? - -- It is easy to overuse `@inject` in places where it doesn't solve any problem -- The problem `@inject`/`inject` method were born to solve is called *dependency hell* -- If you need to **decorate service of some type**, use *Decorator Extension* -- This will lead **to better framework understandability and usability** -- ...and world peace in time :) - -In next article, we will look at other practical use cases for Decorator Extension. - -
- -How do you use `@inject`, constructor injection or Decorator Extension? Let me know in the comments, I'm curious. - -
- -Happy coding! diff --git a/resources/posts/2017/2017-02-15-minimalistic-way-to-create-your-first-nette-extension.md b/resources/posts/2017/2017-02-15-minimalistic-way-to-create-your-first-nette-extension.md deleted file mode 100644 index 5cb41478be6..00000000000 --- a/resources/posts/2017/2017-02-15-minimalistic-way-to-create-your-first-nette-extension.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -id: 28 -title: "Minimalistic Way to Create Your First Nette Extension" -perex: | - Nette extension allows you not only to create open-source packages, but also to split your application to small and logical chunks of code. - - - Open-source extensions are more complex using many Nette\DI features, but today I will show you, how to start with one Nette\DI method and one service only. - ---- - -I consider [Nette Sandbox](https://github.com/nette/sandbox) the best way to show learn any Nette feature. Let's use it. - -## Register service in Nette Sandbox - -If you want to register a service, what will you do? - -```yaml -# app/config/config.neon - -services: - - App\Model\UserManager # put it there -``` - -File `app/config/config.neon` is like a socket. - - - -There is **one place to active your computer**, when you plug it in. - -But what if your want to **plug in computer and mobile charger** in the same time? - - - -To load more services, we use the same interface as `app/config/config.neon`: **a file with service section that lists services**. - - -## Create Local Extension in 5 Steps - -Let's say we want to decouple a FileSystem utilities. - -### 1. Pick a Name for Directory - -What about "FileSystem"? If you agree, create `src/FileSystem` directory. -We will put configuration (sevices.neon) and classes there. - - -### 2. Create or move Related Classes there - -Starting small, one service will do. When it grows, we can decouple it later. - -```php -# src/FileSystem/FileSystem.php - -getContainerBuilder(), - $this->loadFromFile(__DIR__.'/../config/services.neon') - ); - } -} -``` - -### 5. Register it in Application - -```yaml -# app/config/config.neon - -extensions: - - FileSystem\DI\FileSystemExtension -``` - -That's it! - - -## Try it Out - -Now try using `FileSystem\FileSystem` in HomepagePresenter: - -```php -# app/presenters/HomepagePresenter.php - -fileSystem = $fileSystem; - } -} -``` - -and running application: - - - -**Fails**? Damn, I can't put this on my blog. - -Oh, **we need to tell composer about these classes**. He doesn't know, where to find it. -```javascript -{ - "require-dev": { - "..." - }, - "autoload": { - "psr-4": { - "FileSystem\\": "src/FileSystem" - } - } -} -``` - -And manually rebuild `autoload.php` (composer does by default only after `composer update`): - -```bash -composer dump-autoload -``` - -Refresh and... - - - -...it works! - -Phew! That would have been embarrassing. - - -### To Sum Up - -**Now you know how to do your first extension**. - -- create a directory in `/src` -- add `/src//services.neon` -- add `/src/DI/Extension.neon` -- register extension to `app/config/config.neon` -- and extend `autoload` section in `composer.json` (prevents from putting failing code to public blog :)) - -**Let me know if you get stuck somewhere**. I want this tutorial to be as easy to understand as possible. diff --git a/resources/posts/2017/2017-08-07-7-new-features-in-easy-coding-standard-22.md b/resources/posts/2017/2017-08-07-7-new-features-in-easy-coding-standard-22.md deleted file mode 100644 index 900c5b7880b..00000000000 --- a/resources/posts/2017/2017-08-07-7-new-features-in-easy-coding-standard-22.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -id: 49 -title: "7 New Features in Easy Coding Standard 2.2" -perex: | - After extensive cooperation with [David Grudl on Nette\CodingStandard](https://twitter.com/geekovo/status/885152407948333056) ECS got new features with **focus on developer experience**. Smart experience. - - Prepared configs, reduction of config to few lines, `--config` option and more. - -updated_since: "November 2020" -updated_message: | - Switched from deprecated `--set` option to PHP config. - Switched from YAML to PHP configs in **Symplify 9.** ---- - -Today we'll look on **new features it uses from ECS**. - -## 1. Shorter Bin - -```bash -# before -vendor/bin/easy-coding-standard - -# now -vendor/bin/ecs -``` - -## 2. Prepared Rule Sets - -Before you had to name all the checkers manually in your config. There was no *PSR2* group nor *Symfony* like there is in other tools. **Now you can pick from 10 prepared configs**. - -*PHP_CodeSniffer + PHP CS Fixer* - -```php -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\EasyCodingStandard\ValueObject\Option; -use Symplify\EasyCodingStandard\ValueObject\Set\SetList; - -return function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(SetList::PSR_12); - $containerConfigurator->import(SetList::COMMON); - $containerConfigurator->import(SetList::SYMPLIFY); - // ... - // explore the constants on `SetList` class -}; -``` - -## 3. Use Whole Set But 1 Checker - -Imagine you love the PSR-12 set, **except that 1 checker**. Do you skip the set completely or copy all the rules manually except the one? - -Not anymore! - -```php -use PhpCsFixer\Fixer\Operator\UnaryOperatorSpacesFixer; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\EasyCodingStandard\ValueObject\Option; -use Symplify\EasyCodingStandard\ValueObject\Set\SetList; - -return function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(SetList::PSR_12); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::SKIP, [ - // ignore 1 rule everywhere - UnaryOperatorSpacesFixer::class => null, - ]); -}; - -``` - -## 4. Skip More Than 1 File For Specific Checker - -Do you need to skip more files for 1 specific checker? **Now you can [`fnmatch`](https://php.net/manual/en/function.fnmatch.php) pattern**: - -```php -use SlevomatCodingStandard\Sniffs\TypeHints\TypeHintDeclarationSniff; - -// ... -$parameters->set(Option::SKIP, [ - TypeHintDeclarationSniff::class => [ - '*packages/CodingStandard/src/Sniffs/*/*Sniff.php' - ], -]); -``` - -## 5. New Command `Show` Display Used Checkers - -Do you know, what checkers do you use? - -```bash -vendor/bin/ecs show -``` - -This is rather debug or info tool, but it might come handy. - -**You can find [more options of this command in README](https://github.com/symplify/easy-coding-standard)**. - - -## 6. Scan `*.php` and `*.phpt` Files - -EasyCodingStandard checks only `*.php` files by default. But what if you want to check `*.phpt` as well as in case of [Nette\CodingStandard](https://github.com/nette/coding-standard)? - -Use `Option::FILE_EXTENSIONS`: - -```php -// ... -$parameters->set(Option::FILE_EXTENSIONS, ['php', 'phpt']); -``` - -## 7. Are you Tabs Person? - -There you go: - -```php -// ... -$parameters->set(Option::INDENTATION, Option::INDENTATION_TAB); -``` - -### Like it? Try It - -If you find these 7 news useful, try [ECS](https://github.com/symplify/easy-coding-standard) right now. - -
- -Happy coding! diff --git a/resources/posts/2017/2017-09-04-how-apigen-survived-its-own-death.md b/resources/posts/2017/2017-09-04-how-apigen-survived-its-own-death.md deleted file mode 100644 index 4e9d5cc23a4..00000000000 --- a/resources/posts/2017/2017-09-04-how-apigen-survived-its-own-death.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -id: 53 -title: "How ApiGen Survived its Own Death" -perex: | - [ApiGen](https://github.com/apigen/apigen) was broken for a long time. It depended on Reflection package, that was not developed since 2013 and was unable to parse _newer_ code. When I say newer, I mean _hot_ PHP features like `::class` in 5.5\. I don't even talk about 5.6 or 7. - - I got frustrated. I spent a year on a project that is still not working out of the box. So I took spring off to change it. **My goal was to replace reflection or let the project die in peace**. - - This is story about the whole journey of ups and downs. - - -updated_since: "December 2018" -updated_message: | - Important! **ApiGen is [looking for a maintainer](https://github.com/apigen/apigen/issues/1020)**. It might work on smaller projects, but I've heard from David Grudl that `roave/better-reflection` is eating GBs of memory. - - It probably needs to be replaced by a custom reflection + finally release 5.0 stable. - Are you out there? ---- - -Prepare for deep darkness, (almost) burning out and... team work that helped me to make it to the end. - - - - -## Step 1: Bump to PHP 7.1 - -I love PHP 7.1 and I use it everywhere since its release in 2016. This year [more and more big projects are migrating](/blog/2017/06/05/go-php-71/), yet this is still low % of all packages. - -So my condition was to [bump minimal requirement to PHP 7.1](https://github.com/ApiGen/ApiGen/issues/779#issuecomment-285960383). Current maintainers agreed, so I had green on. Thank you [Alexander Jank](https://github.com/jankal) for your support in my first PRs to ApiGen this year. - - -## Step 2: Pick the right Reflection Successor - -ApiGen uses reflection to analyze classes, their methods, interfaces, traits, parent class of the class, their methods etc. - -**Pure PHP reflection can be barely used for advanced tasks like the last one**, so I'd have to rewrite whole package myself. That's not a way to go if you want to have a calm life and normal sleep. - -The question was, **where to find the right one?** - -I knew a few, but not any that would be able to parse PHP 7.1 by itself. I was aware of [nikic/php-parser](https://github.com/nikic/PHP-Parser), that was maintained and future-compatible (Nikic even added PHP 7.2 features recently). But it was only parsing tool, not smart reflection wrapper. - - -### Reaching out for Help - -In that time, I came across [Projects using the PHP Parser](https://github.com/nikic/PHP-Parser/wiki/Projects-using-the-PHP-Parser) on Wiki of PHP-Parse, I consulted with [Jan Tvrdik](https://github.com/jantvrdik) and [Ondrej Mirtes](https://twitter.com/OndrejMirtes). - -This all [led me to a package](https://github.com/ApiGen/ApiGen/issues/817) called [Roave/BetterReflection](https://github.com/roave/better-reflection) by [James Titcumb](https://www.jamestitcumb.com) and [Marco Pivetta](https://ocramius.github.io). - -I'm bit suspicious to projects that were lastly tagged a half year ago, but I felt I could gave it a go. - - -## Step 3: Replace Reflection - -All right, package picked! The ~~fun~~ hell was about to begin. - -Imagine your whole application uses everywhere a package, that got stuck 4 PHP versions ago. You feel it more and more. Everyday you need to patch it because there are other packages that are up-to-date. You know, like PHP itself. - -When I maintained the ApiGen in 2014, I felt I should [interface everything](https://ocramius.github.io/blog/when-to-declare-classes-final) - thank you for that *past me*. All reflection classes were interfaced, and there was somewhat of a bridge between TokenReflection (the old package) and ApiGen value objects for Reflections. - -Still, I could not drop all old reflections without having prepared all new ones. - -But the algorithm alone was simple (well after stepping back from ~3 k lines of code): - -- 1. a 3rd party package's `Reflection` goes in -- 2. ApiGen transforms it -- 3. an ApiGen Reflection comes out - - -### Collector + Transformer + Router to the rescue - -I don't know how that happened, but I managed to combine 3 patterns to make this work. - -By "making this work" I mean: - -- **keep old** reflection package as stable fallback -- **use new reflection package** only on single reflection class, e.g. `ClassConstantReflection` - -The main service that takes cares of this is [TransformerCollector](https://github.com/ApiGen/ApiGen/blob/a6f56691d87f74a64b31a15b7866d5a839aecb60/packages/Reflection/src/TransformerCollector.php#). - -All particular Transformers are **collected** into it. - -When reflection is passed, ApiGen will decide what to do with it - that is [the Router part](https://github.com/ApiGen/ApiGen/blob/a6f56691d87f74a64b31a15b7866d5a839aecb60/packages/Reflection/src/TransformerCollector.php#L68-L71). - -Having this setup ApiGen could: - -- 1. have old TokenReflection -- 2. match specific classes and reparse them with BetterReflection -- 3. output ApiGen Reflection value objects - -In time, I've added more and more BetterReflection transformers, like [ClassReflectionTransformer](https://github.com/ApiGen/ApiGen/blob/a6f56691d87f74a64b31a15b7866d5a839aecb60/packages/Reflection/src/Transformer/BetterReflection/Class_/ClassReflectionTransformer.php#L46) that handles ClassReflection. - -*[10 PRs later...](https://github.com/ApiGen/ApiGen/issues/817)* - -There was light in the of the tunnel. - -**Rule of a thumb: when you need to replace something, build a bridge/router and do it gradually. You'll be both safe and in progress.** - -**Never rewrite running project from scratch**, unless you really have to. - - -## Step 4: Is Ship Going Down? - -I had dilemma about global constants that BetterReflection doesn't support, yet TokenReflection does. I didn't know what to do with that and I was stuck. - -Then, Jan Tvrdik and I have a chat at one grill party about this. Jan helped me to realize, **that single issue should not stand in a way of saving the project**. I could drop it and if anyone needs it, he or she might implement it. That was a huge step forward for me and the project. - - -### Drop Everything You don't Need - Just to Breathe - -I also came to the state, when there was too much coupling of **features I didn't feel like they were useful anymore**. - -Using Markdown in docblock descriptions to highlight them was one of them. I never saw that in any code except ApiGen and I still don't think using Markdown in PHP code is a good idea. - -I proposed issue [of turning Markdown down](https://github.com/ApiGen/ApiGen/issues/782#issuecomment-285988252) and adding event instead, so anyone could implement if really needed and [it solved around 10 issues](https://github.com/ApiGen/ApiGen/pull/795). Just like that. - -**Rule of a thumb? When you're stuck in open-source project, drop things that are the least relevant to the project.** It might give you space to breathe and to move forward to next release. - - -## Step 5. Reflection Replaced - -*10 more PRs later* - -Reflection works - PHP 7.1 code is perfectly parsed with no issues! - - - -It didn't have cool features like tree references, but I was able to parse PHP 5.5, PHP 5.6, PHP 7.0 and PHP 7.1 with no problem at all. We could finally close over 30 opened issues that spread for last 4 years. - -This gave me a dopamine shot, yet worsts was about to come. - - -## Step 6. Burnout - -It was June 2017. The main issue was solved and there was plenty space to improve ApiGen. **But not motivation**. I didn't use it personally and I felt I was there mainly to replace reflection, because I was the one was responsible for its design. - -After I finished that, I was looking for co-maintainer and somebody who's using the project to took over me. - -Personally, I felt ~~bit~~ burned out. What now? Give up the project? Go to cinema? Go to a coach? ✓ - -What helped me was a [release-candidate](https://github.com/dbrock/semver-howto#release-candidates-100-foo). An illusion of milestone, that closes huge part of dev work. - -I released [ApiGen 5.0-RC1](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC1) on June 3rd 2017. It **actually brought more attention then I expected**. Release Candidates are apparently sign that project is back to life. - - - -## Step 7. Saved! - -Situation changed with [5.0-RC3](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC3). - - - -As you can see, [Vlasta Vesely](https://github.com/vlastavesely) did almost 90 % of work on the release. I went to Brno to meet Vlasta, we had great chat and wine - thank you for both. - -**I asked Vlasta to join ApiGen maintainer team and he agreed**. The future is bright now. - - -And that is the whole journey up to present moment. - - -### What would be the main takeway? - -It's all about team work, priorities, communication of miss-understandings and dealing with your own shit you find on the way home. - -Now to the shorter practical part... - -## What changed and what was removed? - -If I should mention 4 most important changes you should know about, it would be: - -- Version 4.0 worked fine with PHP 5.4 code - newer usually crashed it. ApiGen **can deal with PHP 5.5, 5.6, 7.0 and 7.1 code** now. -- Min PHP version bumped **from PHP 5.4 to PHP 7.1**. ApiGen is still able to parse older code. -- **TokenReflection refactored to BetterReflection** - thanks to James and Marco for fast responses on our issues and pull-requests. -- Switched to [Symfony 3.3 Dependency Injection](https://github.com/ApiGen/ApiGen/pull/880) with [time-saving features](/blog/2017/05/07/how-to-refactor-to-new-dependency-injection-features-in-symfony-3-3/) - thanks [Martin Hujer](https://martinhujer.cz) for the idea. - - -Look at particular releases to get complete list of changes. Changelogs are nice and clean: - -- [ApiGen 5.0-RC1](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC1) -- [ApiGen 5.0-RC2](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC2) -- [ApiGen 5.0-RC3](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC3) -- [ApiGen 5.0-RC4](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC4) -- [ApiGen 5.0-RC5](https://github.com/ApiGen/ApiGen/releases/tag/v5.0.0-RC5) - - -## Run ApiGen Yourself - -ApiGen uses BetterReflection that is still not tagged, so you need to install it like: - -```json -{ - "require-dev": { - "apigen/apigen": "5.0.0-RC5", - "roave/better-reflection": "dev-master#7ce58dd" - } -} -``` - -and run with composer: - -```bash -composer update -``` - -And you are read to go. - - -Happy generating! diff --git a/resources/posts/2017/2017-11-27-united-php-71-adoption-6-months-later.md b/resources/posts/2017/2017-11-27-united-php-71-adoption-6-months-later.md deleted file mode 100644 index 4afd75e4fe6..00000000000 --- a/resources/posts/2017/2017-11-27-united-php-71-adoption-6-months-later.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -id: 64 -title: "United PHP 7.1 Adoption 6 Months Later" -perex: | - A year since it's release and 6 months since GoPHP71 initiative. - PHP 7.1 is **the fastest adopted** minor version of PHP already beating PHP 7.0. - - - How is adoption going in open-source and why it should continue from bleeding-edge projects? ---- - -
- -
- "United we stand, divided we fall." -
ancient Greek storyteller Aesop
-
- -
- - - - - -## How is GoPHP71.org Doing? - - -I've created a page gophp71.org half year ago, inspired by gophp7.org and by [Go PHP 5](https://www.garfieldtech.com/blog/go-php-5-go) - read this one if you care about background values of this movement. - - -In [release post](/blog/2017/06/05/go-php-71/) I've explained **why right to PHP 7.1** and not PHP 7.0, how important is **united community** in this and how this can **bring positive energy to open-source** and as well host providers upgrades. - -
- -From 2 projects in *June 2015*: - - - -Now there are **11 projects**, including *big 3* - Symfony, Doctrine and Laravel. - - - - - -Is your project missing? [Go and it!](https://github.com/TomasVotruba/gophp71.org/edit/master/_data/projects.yaml) - - - -### Prove beats Promise - Packagist Stats - - -To support "PHP 7.1 is the fastest adopted minor version of PHP" statement, [Jordi](https://seld.be) recently released [PHP Versions Stats - 2017.2 Edition](https://seld.be/notes/php-versions-stats-2017-2-edition) with very nice result from packagist stats: - - - - - -## Great Job, PHP Community! - - -It makes me very happy, that **people from PHP community are able to [synchronize](/blog/2017/10/30/what-can-you-learn-from-menstruation-and-symfony-releases/)** despite their different opinions on things. -
- - -### Special Thanks to Doctrine Project - -I really loved this [Doctrine bump PHP 7.1 announcement](http://www.doctrine-project.org/2017/07/25/php-7.1-requirement-and-composer.html). I completely agree with "Why dropping PHP support in a minor version is not a BC break" part. If you think PHP version bump is BC break, you should read it. - -I admit [I wasn't nice](/blog/2017/03/27/why-is-doctrine-dying/) to Doctrine Project this Spring and **I'm sincerely sorry about that**. I'm trying to [influence this better way](/blog/2017/10/16/how-to-use-repository-with-doctrine-as-service-in-symfony/). - -Ever since I **see Doctrine community are doing great** - from [removing YAML references](https://github.com/doctrine/doctrine2/pull/5932), to [cleaner Symfony support](https://github.com/doctrine/DoctrineBundle/pull/727). - -## But I want to go PHP 7.0 - -Still not convinced about reasons? - -[@dmonllao](https://github.com/dmonllao) poses an idea: *I want to take is slowly and go only to PHP 7.0*. - -Let me explain how that could influence PHP ecosystem and slow down productivity of many projects: - -- Imagine that in 6 months all of those 11 projects on gophp71.org will **require PHP 7.1 in their LTS versions**. -- *Moodle* (could be any other package, it's just example) decides to go with **PHP 7.0**. -- If you work with PHP, there is quite big chance you'll be using at least one of those 11 packages. -- Let's say you want to use newest features + LTS, so you **bump your local nad server to PHP 7.1**. - -All good for now, but then: - -- You need to use *Moodle* in your project. **Its code contains only PHP 7.0 features**. -- Your code naturally **extends or implements 3rd party classes**. You can use PHP 7.1 on most of them - e.g. `void` and nullable typehints of interfaces. -- But then your need to extends *Moodle*'s code and **you have to be careful and use only PHP 7.0 features**. Features like `void` or `nullable` would break it. - - -### Result? Double Measures & Dichotomic Coding - -- You have to have **2 different coding standards** - one for PHP 7.0 and one for PHP 7.1 with various paths to scan. -- If you use static analysis like [PHPStan](/blog/2017/01/28/why-I-switched-scrutinizer-for-phpstan-and-you-should-too/), you have to have 2 configs again to validate code properly. -- 2 testing approaches etc. - - -And that's **only 1 package with different PHP version**. Imagine there would another package that requires PHP 7.2... or if you combine PHP 7.0 and PHP 7.1 interface in single class. - - - -## But I don't want to Drop Support for PHP 7.0 - - - -I've borrowed this amazing picture from [Jordi](https://seld.be/notes/php-versions-stats-2016-2-edition). - -You **can keep support for older PHP version** even if you bump minimal requirement to PHP 7.1, just won't add new features to them. - - - -### Spread the Word - -At the moment only 4 projects on are tagged and it will take some timer before this becomes mainstream. Yet, we can see obvious trend moving to PHP 7.1 as minimal requirement. **Thanks to community and people that are bold enough to ask the question** or [even sending a PR](https://github.com/laravel/framework/pull/21995). - - -
- - -**If you see some next project bumping to PHP 7.0, think about possible consequences of that decision.** - -

- -Happy bumping! diff --git a/resources/posts/2018/2018-03-26-new-in-easy-coding-standard-4-clean-symfony-standard-with-yaml-and-services.md b/resources/posts/2018/2018-03-26-new-in-easy-coding-standard-4-clean-symfony-standard-with-yaml-and-services.md deleted file mode 100644 index ce4d96aa288..00000000000 --- a/resources/posts/2018/2018-03-26-new-in-easy-coding-standard-4-clean-symfony-standard-with-yaml-and-services.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -id: 86 -title: "New in Easy Coding Standard 4: Clean Symfony Standard with Yaml and Services" -perex: | - I wrote about [news in Easy Coding Standard 3](/blog/2018/03/01/new-in-symplify-3-4-improvements-in-easy-coding-standard/) a while ago. EasyCodingStandard 4 is released yet (still in alpha), but soon you'll be able to use all the news I'll show you today. - - - And what are they? Neon to YAML, semi-static to Services, customizable caching, even simpler skipper, short bin and more. - - -updated_since: "August 2020" -updated_message: | - Updated ECS YAML to PHP configuration since **ECS 8**. ---- - -## 1. Configure Caching Directory - - - Check the PR #661 - - -Docker users will be happy for this feature, since it makes ECS much more usable. To enjoy speed of caching of changed files on second run, just tune your config. - -```php -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set('cache_directory', '.ecs_cache'); -}; -``` - -Thank you [Marcin Michalski](https://github.com/marmichalski) for adding this feature. - -
- -## 2. Skip Anything, Anywhere - - - Check the PR #661 - - -One of the features I really like is skipping particular spots. PHP CS Fixer and PHP_CodeSniffer can ignore whole directory, 1 sniff everywhere or force to put annotation to your code and that's not the way to go. **Your code should have no idea about tools you use to analyze it**. - -What you really need? Exclude 1 file but only for 1 checker. Or 1 checker for group of files and sometimes only 1 code from sniff on 1 file. That all is possible now. - -**Because details matters and it's pointless to think about code or class**, you can now remove `skip_codes` key from your config and use `skip` section only. - -
- -## 3. Short `vendor/bin/ecs` is the King - - - Check the PR #647 - - -One last detail. Did you use this bin file to run ECS? - -```bash -vendor/bin/easy-coding-standard -# or -vendor/bin/easy-coding-standard.php -``` - -I know it's pain, mainly during live demo presentations with all that tyops :). - -Now this is the only way to use ECS: - -```bash -vendor/bin/ecs -``` - -Typo proof or at least less error prone. Just change it in you [`composer.json`'s `script` section](https://blog.martinhujer.cz/have-you-tried-composer-scripts) or CI setups and you're ready to go! - -
- -## 4. DI Migration Finished: From Neon to YAML - - - Check the PR #651 - - -Symplify used `Nette\DI` a long time ago and with it its markup language - Neon. Then it moved to `Symfony\DependencyInjection` in [Symplify 2.0](https://github.com/symplify/symplify/blob/master/CHANGELOG.md#v200---2017-06-16), because it was just impossible to reject [all these awesome Symfony 3.3 features](/blog/2017/05/07/how-to-refactor-to-new-dependency-injection-features-in-symfony-3-3/) by Nicolas Grekas. But this was just partial migration - Neon files still worked. - -That lead to situation, where 5 *custom-cool-classes* simulated loading transforming Neon to YAML format, merging it and then passing to Symfony Container, hoping all went well. And it worked. Well, most of the times. - -Based on feedback from [community around Symplify](https://github.com/symplify/symplify/issues/565), rejection of ECS in [Doctrine\CodingStandard](https://github.com/doctrine/coding-standard) where Neon was one of reasons and weird feeling from promoting "local-only standard", I decided to move to Symfony completely. - - - - - -I had one problem - missed services autocomplete in Yaml files. But you know what they say: - -
- There are no solutions. There are only trade-offs -
- -I hear you community, so lets trade! **From ECS 4, you can use Yaml everywhere with syntax you know, behavior from Symfony ecosystem you know and with no need to learn new standard.** - -### How to Migrate? - -Well just rename `easy-coding-standard.neon` or `ecs.yml` and - then read about it in [Neon vs. Yaml and How to Migrate Between Them](/blog/2018/03/12/neon-vs-yaml-and-how-to-migrate-between-them/). - -
- -## 5. From Semi-Static Checkers to Services as First-Class Citizen - -**Note: Symplify 8 now uses PHP configuration.** - - - Check the PR #660 - - -Thanks to Yaml, we could use finally use full power of Symfony\DependencyInjection component, constructor injection, autowiring... again, all that you probably already know from Symfony. - -Why? **ECS is basically a Symfony application with DI Container**. It loads all checkers from config you provide, turns them into services and then uses those services to check the code. - -YAML was the only missing part to do this. And ECS has it now, so does the explicit services! -And you can do and use any feature you Symfony know. Magic no more #metoo. - -

- -Happy upgrading! diff --git a/resources/posts/2018/2018-05-24-boss-vs-masseuse-way-to-add-coding-standards-to-big-project.md b/resources/posts/2018/2018-05-24-boss-vs-masseuse-way-to-add-coding-standards-to-big-project.md deleted file mode 100644 index 28ef31f280c..00000000000 --- a/resources/posts/2018/2018-05-24-boss-vs-masseuse-way-to-add-coding-standards-to-big-project.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -id: 108 -title: "The Boss vs. The Masseuse Way to Add Coding Standards to a Big Project" -perex: | - Do you prefer a **boss who's watching you** how you sit at the desk telling how to sit right - or a **masseuse who's taking care of your hands** tired from programming with her gentle hands? - - - When it comes to coding standards, the love and fun is the best experience with it. Let's look how such "masseuse" can be added to your big project. - - -updated_since: "February 2021" -updated_message: | - Use new Symplify 9 syntax for PHPStan rule registration. ---- - -## "The Boss" Way to Add Coding Standard - -It's very rare that projects have coding standards right from the first line. That applies to CI, tests, coverage, and docs. Why? They come with experience and with a need. **The biggest added value of coding standards is to bring more fun to your team, as it works for you**. - -Saying that **the most projects need and then add coding standards when they grow up to a large code base**. - -The most [popularized way](https://akrabat.com/checking-your-code-for-psr-2) to do this is: - -```bash -composer require squizlabs/php_codesniffer --dev -vendor/bin/phpcs --standard=PSR2 /app /src -``` - -I bet you're able to run these command even if you see it for the first time. - -But what will happen next? - - - -**You'll get ~ *X* hundreds of errors you don't understand**. It can feel embarrassing like having the boss' eyes on you all the time. - -This is often the reason coding standard is not part of many great PHP projects, which makes me very sad. - -## "The Masseuse" Way to Add Coding Standard - -How to make this first experience better? Start slowly, one touch at a time, like a masseuse with your hands. - -### 1. Install Your Favoring Coding Standard Tool - -For me, it's obviously [ECS](https://github.com/symplify/easy-coding-standard): - -```bash -composer require symplify/easy-coding-standard --dev -``` - -### 2. Use One Sniff/Fixer that Helps You The Most - -This is the most important step. This checker should be - -- easy to understand -- helpful for you as a programmer (not a `{` or `"` position) -- helpful to your project -- and easy to fix code for you (like `array()` → `[]`) - -### 3. Make it Pass Without Any Code Change - -Last week a [Cognitive Complexity Rule](/blog/2018/05/21/is-your-code-readable-by-humans-cognitive-complexity-tells-you/) was published there was [very positive feedback](https://github.com/symplify/symplify/issues/834) on it. - -**If your coding standard should have only 1 rule - this is the one.** - -```yaml -# phpstan.neon -includes: - - vendor/symplify/phpstan-rules/packages/cognitive-complexity/config/cognitive-complexity-services.neon - -services: - - - class: Symplify\PHPStanRules\CognitiveComplexity\Rules\FunctionLikeCognitiveComplexityRule - tags: [phpstan.rules.rule] - arguments: - maxMethodCognitiveComplexity: 8 -``` - -But when you run your tool (`vendor/bin/phpstan analyse /src`), it will probably drop dozens of errors. And we don't want to go to the boss approach. - -
- -Saying that, **we make the rule so free, that your code passes it**: - -```diff - services: - - - class: Symplify\PHPStanRules\CognitiveComplexity\Rules\FunctionLikeCognitiveComplexityRule - tags: [phpstan.rules.rule] - arguments: -- maxMethodCognitiveComplexity: 8 -+ maxMethodCognitiveComplexity: 50 -``` - -**0 errors!** - -You can now add this to your GitHub Actions and make the PR. Merge it and take a 2 weeks break. - -
- -Then decrease the criteria for 10 %: - -```diff - services: - - - class: Symplify\PHPStanRules\CognitiveComplexity\Rules\FunctionLikeCognitiveComplexityRule - tags: [phpstan.rules.rule] - arguments: -- maxMethodCognitiveComplexity: 8 -+ maxMethodCognitiveComplexity: 45 -``` - -- Fix couple the cases that will appear. -- One at a time. -- Then repeat previous workflow: PR, merge and take a week break. - -When **you feel ready**, you can add 1 more checker, make a rule more strict... you get the idea to enjoy your massage :). - -## Proven Practice - -This way I was able to add coding standards to quite a big codebase in [Lekarna.cz](https://github.com/lekarna) a few years ago with not many troubles, and learn how they work along the way. - -
- -**I wish you the same experience in your huge project.** - -

- -Happy coding! diff --git a/resources/posts/2018/2018-08-20-painful-experience-over-solutions-extend-configuratin-in-easy-admin-bundle-with-collector.md b/resources/posts/2018/2018-08-20-painful-experience-over-solutions-extend-configuratin-in-easy-admin-bundle-with-collector.md deleted file mode 100644 index f30aef40d81..00000000000 --- a/resources/posts/2018/2018-08-20-painful-experience-over-solutions-extend-configuratin-in-easy-admin-bundle-with-collector.md +++ /dev/null @@ -1,322 +0,0 @@ ---- -id: 133 -title: "Painful Experience over Solutions: Extend Configuration in Easy Admin Bundle" -perex: | - *Use SOLID to write Clean Code...* Are you tired of theoretical post about how to do achieve some therm? So am I. -
- Instead, let's **dive into real problems I came across while coding and let the code speak the theory between lines**. - - - Today we try to add own config option to YAML of Easy Admin Bundle (without pull-request to the package). ---- - -
- Hindsight is 20/20. -
- -Instead of writing about solution how to do and how awesome I am to know the solution right from the start of this page, I start right from the beginning, where I know nothing about it just like you. - -
- -### The Application - -I'm coding an open-sourced training platform build Symfony 4.2 and Doctrine 2.7 for [Pehapkari community training](https://github.com/pehapkari). It's fully open-sourced on Github under the typical open-source name - [Open Training](https://github.com/tomasvotruba/open-training). - -Admin is just a CRUD to maintain few entities, so I use [EasyAdminBundle](https://github.com/easyCorp/EasyAdminBundle) to handle forms, grids, update, create, delete actions in controllers for me. Huge thanks to [Javier Eguiluz](https://github.com/javiereguiluz) for this amazingly simple and powerful idea. - - - -### The Need - -There is `Training` entity with `name` and relation to `TrainingTerm` entity: - -```php - - -### But... - -After 2 hours I need to add `price`. - -```diff - - There are no solutions. Just trade-offs. - - -I stop and think a bit. How can I write less code to prevent possible bugs and make changes as effective as possible? -**I see there are fewer properties to exclude than properties to include**, by 1:10. It would not be perfect code, but still 10 times safer and more effective code. Worth it! - -```diff - easy_admin: - entities: - Training: - class: 'App\Entity\Training' -- fields: ['name', 'price', 'capacity', 'duration', 'perex', 'description', 'place', 'trainer'] -+ exclude_fields: ['trainingTerms'] -``` - -### Make that Happen and Face False Expectations - -But is that `exclude_fields` or `excluded_fields` or maybe `skip_fields`? I want to see the documentation, so I Google [*easy admin bundle excludes fields*](https://www.google.cz/search?q=easy+admin+bundle+exclude+fields&oq=easy+admin+bundle+exclude+fields&aqs=chrome..69i57.4891j0j7&sourceid=chrome&ie=UTF-8). I find [*Exclude fields in list fields* issue in EasyAdminBundle](https://github.com/EasyCorp/EasyAdminBundle/issues/589). I read it and see the content **is not what I need**. It looks like this option is not supported. I'm sad. What now? - -
- -Open-source packages are closed to extension more than you'd expect. To add one custom feature, you have to basically copy and extend the whole class or use reflection. It's not **because it's difficult to create an extendable code, it's because nobody *believes* it can be done in a nice way**. It *can*, just keep reading. - -
- -Being that suspicious I start my *inner over-engineer* voice: - -- "Create own extension that will hack into the `EasyAdminExtension` and get the config and add `exclude_fields` option" -- "Create own `BetterEasyAdminBundle` that will be run before the `EasyAdminBundle` and will pass parameters there" - -**This might end-up wasting many hours** on custom and useless solution (like "create own Doctrine" idea, true story). Instead I try to invest a bit more time and I continue the brainstorming: - -- "Send pull-request with this feature to the core code" - -Slightly better, but what if Javier doesn't like it? Or what if he's on holiday for 3 weeks? I know, it's summer and very rare to happen, but I have to finish the app in 2 weeks and I don't want to think about bugs like these in the meantime. The **least I can do is to create [an issue](https://github.com/EasyCorp/EasyAdminBundle/issues/2325)** with this idea and my reasons for it. - -## Wander in the Code - -I need a solution and I need it today. What can I do? No hacking, no pull-request, just looking for something in files: - - - -Do you think this is just a random screen-shot not worth your attention? - -- there is a checkbox in *Match case* because `'fields'` is lowercased and we want to focus on that only (no properties or methods with `Fields`) -- there is a limit to `*.php` because that *would be probably* place to extend -- there is a limit to *Directory*: `/../vendor/easycorp`, because we want to hack into this package -- there is `fields` word in search; later I improve it to `'fields'` to narrow results, because we know it's a string - -
- -**I still have no idea about the solution I'll pick. I'm only randomly looking for the light, blindfolded in a dark foggy forest.** -This is called *creative chaos* in coaching circles and it's the most important part of the client's work. - -
- -I scroll down a bit looking at both code and the file name. Suddenly, the fog starts slowly disappearing... - - - -I notice `*ConfigPass` suffix. Is that like `CompilerPassInterface`, a collector-pattern used in Symfony to modify services in the container? - -Being curious I open `NormalizerConfigPass.php` file: - -```php - - -I see a tag: `easyadmin.config_pass`. Let's look for that string: - - - -Warmer! **I've just found a collector pattern.** To config, I look for service under `easyadmin.config.manager` name - [`ConfigManager`](https://github.com/EasyCorp/EasyAdminBundle/blob/07194017918aebe382e1ab0e53c68f6242547a0e/src/Configuration/ConfigManager.php#L112-L119) and look for `foreach` on collected services: - -```php -private function doProcessConfig($backendConfig): array - { - foreach ($this->configPasses as $configPass) { - $backendConfig = $configPass->process($backendConfig); - } - - return $backendConfig; -} -``` - -Bingo! **That means, when I register a service with `easyadmin.config_pass` tag, I'll be able to read and modify the YAML configuration**. - -So I register a service: - - -```yaml -services: - ExcludeFieldsConfigPass: - tags: - - - name: "easyadmin.config_pass" - priority: 120 # it took me more time to figure out if -100 or 0 or 100 or 1000 means "the first" -``` - -That does 1 thing: - -`fields` (value to be set) = entity properties − `exclude_fields` (value I set in the config) - -
- -It allows me to do simplify `config/packages/easy_admin.yaml` config: - -```diff - easy_admin: - entities: - Training: - class: 'App\Entity\Training' -- fields: ['name', 'price', 'capacity', 'duration', 'perex', 'description', 'place', 'trainer'] -+ exclude_fields: ['trainingTerms'] -``` - -You can see full code of [`ExcludeFieldsConfigPass` on Github](https://github.com/TomasVotruba/open-training/pull/7/files#diff-318660bf4cd1ad8a5d0e608e94df8fae). - -Very smart move Javier - thank you! - -## Learn 1 Algorithm instead of 10 Solutions - -And that's all folks. I hope I've shown you how to approach problems and how to find a way in situations you're the first time in. -The same way I don't memorize Wikipedia and just Google it instead, **I don't remember solutions to 100 PHP problems, but have a couple of algorithms to approach problem solving**. - -- Try A - Failed? -- Try B - Failed? -- Try C - Failed? -- Take a break to prevent [learned helplessness](https://en.wikipedia.org/wiki/Learned_helplessness) :) -- Try D - Failed? -- Try E - Failed? -- Try F - Kaboom! It works! - -
-If artificial intelligence could figure this all out for us, we'd be screwed :). - -Btw, are you coming to [Human Level AI Conference](https://www.hlai-conf.org) in Prague this weekend? I'll be there and I'd be happy if you stop me and say Hi! - -
- -Happy solving! diff --git a/resources/posts/2018/2018-09-20-new-in-symplify-5-3-new-cool-features-of-package-builder.md b/resources/posts/2018/2018-09-20-new-in-symplify-5-3-new-cool-features-of-package-builder.md deleted file mode 100644 index 4daf493c052..00000000000 --- a/resources/posts/2018/2018-09-20-new-in-symplify-5-3-new-cool-features-of-package-builder.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -id: 145 -title: "New in Symplify 5: 3 New Cool Features of PackageBuilder" -perex: | - [PackageBuilder](https://github.com/symplify/package-builder) was always sort of meta package with all **the cool and shiny features anyone can use**. After all, it's the most downloaded Symplify package hitting almost [1000 downloads a day](https://packagist.org/packages/symplify/package-builder/stats). - - In Symplify 5 now it allows you to **drop manual binds** from Symfony configs, separate files from directories **in one method** and merge nested YAML parameters **with 1 service**. ---- - -You don't have this package installed yet? - -```bash -composer require symplify/package-builder -``` - -Now enjoy the news ↓ - -## 1. Drop Manual Binds in Symfony configs - - - See pull-request #998 - - -You can add [parameter binding since Symfony 3.4](https://symfony.com/blog/new-in-symfony-3-4-local-service-binding): - -```yaml -services: - _defaults: - bind: - $meetupComApiKey: '%meetup_com_api_key%' -``` - -That's nice. But what if you have multiple configs that use multiple parameters? - -```yaml -services: - _defaults: - bind: - $meetupComApiKey: '%meetup_com_api_key%' - $facebookApiKey: '%facebook_api_key%' - $maxPostOnHomepage: '%max_post_on_homepage%' - - App\FirstPackage\: - resource: .. -``` - -```yaml -services: - _defaults: - bind: - $facebookApiKey: '%facebook_api_key%' - $maxPostOnHomepage: '%max_post_on_homepage%' - - App\SecondPackage\: - resource: .. -``` - -### Not for Lazy Programmer - -This way you'll be writing more bindings than there are parameters. And there is more! When you remove autodiscovered service that depends on a bound parameter, you'll get this "nice" exception: - -
- Unused binding "maxPostOnHomepage" in service "App\SomeUnterlatedService" -
- -You can solve this all by having a huge config with all parameters, binding and services. Even if the config would be shorter than 100 lines, you still have to maintain parameters, bindings and services and it teaches other programmers to *put-everything-to-one-place* instead of SOLID principles. - -
- -**Would you like to get rid of this all extra maintenance, and just code cleanly instead?** I would! - -So, have you noticed the pattern? - -- `$variableName` <=> `%parameter_name` -- `camelCase` <=> `underscore_case` - -And exactly this can be automated! Just add `Symplify\PackageBuilder\DependencyInjection\CompilerPass\AutoBindParametersCompilerPass` compiler pass: - -```diff -addCompilerPass(new AutoBindParametersCompilerPass()); -+ } - } -``` - -And if you keep this convention, you can keep yours configs clear and minimalistic: - -```diff - services: -- _defaults: -- bind: -- $meetupComApiKey: '%meetup_com_api_key%' -- $facebookApiKey: '%facebook_api_key%' -- $maxPostOnHomepage: '%max_post_on_homepage%' -``` - -Of course you can bind your parameters manually: - -```yaml -services: - _defaults: - bind: - $anotherName: '%non_standard_parameter_naming%' - - SomeClass: - arguments: - - '%very_specfici_meetup_com_api_key%' -``` - -## 2. Separate Files from Directories - - - See pull-request #963 - - -Do you need to work multiple file/directories arguments? - -```bash -vendor/bin/ecs check src tests/ThisFile.php -``` - -Just use `Symplify\PackageBuilder\FileSystem\FileSystem`: - -```php -separateFilesAndDirectories($sources); - -// ... -``` - -## 3. Merge Parameters without Leaving Any Behind - - - See pull-request #989 - - -At the moment, Symfony is unable to merge nested parameters [for historical and other reasons](https://github.com/symfony/symfony/issues/26713): - -```yaml -# config.yml -imports: - - { resource: 'imported-file.yml' } - -parameters: - festivals: - - three -``` - -```yaml -# imported-file.yml -parameters: - festivals: - - one - - two -``` - -This will end up with just 1 festival in classic Symfony Applicatoin. Do you want to use the **full power of YAML, glob and imports** and **still keep all the parameters**? - -Use `Symplify\PackageBuilder\Yaml\ParameterMergingYamlLoader`: - -```php -loadParameterBagFromFile( - __DIR__ . '/config.yml' -); - -var_dump($parameterBag->get('festivals')); -// ['one', 'two', 'three'] -``` - -
- -And that's all folks! - -
- -Happy tuning of your code! diff --git a/resources/posts/2018/2018-10-04-new-in-symplify-5-public-method-order-and-external-final-in-coding-standard.md b/resources/posts/2018/2018-10-04-new-in-symplify-5-public-method-order-and-external-final-in-coding-standard.md deleted file mode 100644 index dbd76a0f621..00000000000 --- a/resources/posts/2018/2018-10-04-new-in-symplify-5-public-method-order-and-external-final-in-coding-standard.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -id: 146 -title: "New in Symplify 5: Public Method Order and External Final in CodingStandard" -perex: | - Coding Standard 5 replaced fixers that renamed classes with more tolerant sniffs. What else is there? -
- New config options **that shorten your config file** and **2 new checkers to keep your code in order**. - -updated_since: "August 2020" -updated_message: | - Removed unsupported rules in Coding Standard 8, add PHPStan rule that handle it better. ---- - -Don't you have this package installed yet? - -```bash -composer require symplify/coding-standard --dev -``` - -Now enjoy the news ↓ - -## 3. Final for 3rd Party Classes - -If you're strict enough to `final` or `abstract` everywhere, you'll love this. Sometimes 3rd party code is not `final`, but you'd love to never see that class in your code - Abstract Controller, Abstract Doctrine Repository or Abstract Object. - -Those `abstract` classes are full of **magic everyone has to [remember](/blog/2018/08/27/why-and-how-to-avoid-the-memory-lock/)**. What if you could **prevent that spreading to your code without constant code-reviews**? - -Let PHPStan rule do the job: - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\ForbiddenParentClassRule - -parameters: - symplify: - forbidden_parent_classes: - - 'Doctrine\ORM\EntityRepository' - - 'Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository' -``` - -This will prevent over-inheritance and embrace composition - like in [Repositories as Services](/blog/2017/10/16/how-to-use-repository-with-doctrine-as-service-in-symfony/) approach: - -❌ - -```php -entityRepository = $entityRepository; - } -} -``` - -
- -That's all folks. Happy sniffing! diff --git a/resources/posts/2018/2018-10-08-new-in-symplify-5-create-merge-and-split-monorepo-with-1-command.md b/resources/posts/2018/2018-10-08-new-in-symplify-5-create-merge-and-split-monorepo-with-1-command.md deleted file mode 100644 index c45ee17697d..00000000000 --- a/resources/posts/2018/2018-10-08-new-in-symplify-5-create-merge-and-split-monorepo-with-1-command.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -id: 143 -title: "New in Symplify 5: Create, Merge and Split Monorepo with 1 Command" -perex: | - Do you want to create, validate and manage your monorepo like a pro? There is no science behind it, just a few routine steps that you need to repeat. - - - And now there is a **tool that will handle these steps for you**. - -updated_since: "December 2020" -updated_message: | - Removed the deprecated `split` command. Use faster and reliable [GitHub Action](/blog/2020/11/09/new-in-symplify-9-monorepo-split-with-github-action/) instead. ---- - -This package was initially released in Symplify 4.5, but it took some time to test in practice, remove WTFs and be sure it contains all people need. - -Now there are 7 commands in total, but today we'll focus **on the 4 most important ones**. - -First, install this package to an empty repository: - -```bash -composer require symplify/monorepo-builder --dev -``` - -## 1. Create Your Monorepo - -You'll run this command **just once** to create a monorepo in the empty repository. - -```bash -vendor/bin/monorepo-builder init -``` - -It will prepare basic monorepo structure with 2 packages: - -```bash -/packages - /first-package - /src - composer.json - /second-package - /src - composer.json -composer.json -monorepo-builder.yml # basic configuration -``` - -Use `composer.json` as you know it for most sections. But to manage **`require`, `require-dev`, `autoload` and `autoload-dev`** sections use only `/packages/first-package/composer.json`, `/packages/second-package/composer.json` like they were standalone packages. - -Extra - mostly-dev - dependencies are managed by `monorepo-builder.yml`. - -Is that unclear to you? Don't worry, you'll see how it works in `merge` command below. - -## 2. Validate it - -This command will tell you if your dependency versions are the same in every packages' `composer.json` and in root `composer.json`. - -```bash -vendor/bin/monorepo-builder validate -``` - - - -You don't have to run this command manually since it's included in the next one. - -## 3. Merge `composer.json` - -```bash -vendor/bin/monorepo-builder merge -``` - -Here you'll understand the magic from `init` command. With `merge` command, the Monorepo Builder will join all packages' `composer.json` - after validation of course. - -And what about extra dependencies like PHPUnit, coding standards and static analysis? - -```yaml -# monorepo-builder.yml -parameters: - # for "merge" command - data_to_append: - require-dev: - phpunit/phpunit: '^7.3' -``` - -That's what `data_to_append` section is for. These packages and versions will be added to `composer.json`. - -*Btw, all packages are nicely sorted by name so you always find them quickly.* - - -### Do you Want to Know More? - -Discover other commands: - -```bash -vendor/bin/monorepo-builder -``` - -and read [`README`](https://github.com/symplify/monorepobuilder) for detail usage and tricks. - -
- -Happy monorapping! diff --git a/resources/posts/2018/2018-11-29-how-to-manage-configuration-in-symfony-without-bundle-extension-and-configuration.md b/resources/posts/2018/2018-11-29-how-to-manage-configuration-in-symfony-without-bundle-extension-and-configuration.md deleted file mode 100644 index 111a0f9b233..00000000000 --- a/resources/posts/2018/2018-11-29-how-to-manage-configuration-in-symfony-without-bundle-extension-and-configuration.md +++ /dev/null @@ -1,379 +0,0 @@ ---- -id: 163 -title: "How to Manage Configuration in Symfony without Bundle, Extension, and Configuration?" -perex: | - Symfony Flex is moving towards of bundle-less applications. That doesn't mean you should create a monolith code in `/src` as fast as possible, but rather control everything via `.yaml` and `.env` files. It's takes [few steps](/blog/2017/05/07/how-to-refactor-to-new-dependency-injection-features-in-symfony-3-3/#refactor-service-config-in-5-steps) to **remove extension and move to import** of `services.yaml`. - - But how would you approach a simple task **as *setup an account number* parameter**? - ---- - -If you hear about the **trend of "no-bundle" application** for the first time, is very nicely summarized in 10 points in [SymfonyCasts](https://symfonycasts.com/blog/AppBundle). Go check it, I'll wait here. - - - -## 1. How this Affected Service Registration? - -Before you need 3 classes to get services to the application: - -```bash -/app - AppKernel.php -/packages - /accountant - /src - /DependencyInjection - AccountantExtension.php - AccountantBundle.php - /config - services.yaml -``` - -```php -load('services.yaml'); - } -} -``` - -```yaml -# packages/accountant/config/services.yaml -services: - Project\Accountant\: - resource: "../src" -``` - -Now we can drop all of the PHP [magic code](https://matthiasnoback.nl/2013/10/symfony2-some-things-i-dont-like-about-bundles) down: - -```diff - /app - AppKernel.php - /packages - /accountant - /src -- /DependencyInjection -- AccountantExtension.php -- AccountantBundle.php - /config - services.yaml -``` - -...and load services in local config: - -```yaml -# app/config.yaml -imports: - - { resource: "packages/accountant/config/services.yaml" } -``` - -Or we can set this up just once for all [local packages](/blog/2017/12/25/composer-local-packages-for-dummies/) with [glob](https://symfony.com/blog/new-in-symfony-3-3-import-config-files-with-glob-patterns): - -```yaml -# app/config.yaml -imports: - - { resource: "packages/*/config/services.yaml" } -``` - -**We deleted all PHP files and add 2 lines to config** - that's what a good trade, right? Much less code can go wrong and the result is easy to read even for a programmer who was just hired today. - -
- -I think most of you already know this configuration shift and use it for months, right? Now the harder part, that many people still struggle with. - -## 2. How this Affected Configuration? - -In the "accountant" package we have a service that sends money... no ordinary money, Bitcoins! And we need to **set an account number parameter** to it: - -```php -accountNumber = $accountNumber; - } - - public function donateTo(float $amount, string $targetAccountNumber) - { - // move $amount - // from $this->accountNumber - // to $targetAccountNumber - } -} -``` - -The configuration of `$accountNumber` value in [*bundle-school* paradigm](https://stovepipe.systems/post/creating-bundle-configuration) looks like this: - -```diff - # packages/accountant/config/services.yaml -+accountant: -+ account_number: "123_secret_hash" -+ - services: - Project\Accountant\: - resource: "../src" -``` - -```php -processConfiguration($configuration, $configs); - - // for bitcoin sender - $container->getDefinition('Project\Accountant\BitcoinSender') - ->setArgument('accountNumber', $config['account_number']); - - // for further use (optional) - // $container->setParameter('account_number', $config['account_number']); - } -} -``` - -```php -root('accountant'); - $rootNode - ->children() - ->scalarNode('account_number') - ->end() - ->end(); - - return $treeBuilder; - } -} -``` - -All this fuss just to **load single parameter**? Not anymore: - -```diff - /app - AppKernel.php - /packages - /accountant - /src -- /DependencyInjection -- AccountantExtension.php -- AccountantConfiguration.php -- AccountantBundle.php - /config - services.yaml -``` - -All cleaned up. We run the app and... - -*ERROR: "$accountNumber" argument was not set* - -Damn! What now? - -## What Options do We Have? - -### 1. Keep the Extension - -```php -getDefinition('Project\Accountant\BitcoinSender') - ->setArgument('accountNumber', $config['account_number']); -``` - -❌ - -We want to get rid of this code, not to maintain it. - -### 2. Set Parameter Manually in the Config - -```diff - # packages/accountant/config/services.yaml --accountant: -+parameters: - account_number: "123_secret_hash" - - services: - Project\Accountant\: - resource: "../src" - -+ Project\Accountant\BitcoinSender: -+ arguments: -+ $accountNumber: "%account_number%" -``` - - - - -We want config to use PSR-4 autodiscovery to it's fullest potential, not go back to manual service definitions. - -### 3. Bind the parameter - -Good idea! Since [Symfony 3.4](https://symfony.com/blog/new-in-symfony-3-4-local-service-binding) we can do this: - -```diff - # packages/accountant/config/services.yaml --accountant: -+parameters: - account_number: "123_secret_hash" - - services: -+ _defaults: -+ bind: -+ $accountNumber: "%account_number%" -+ - Project\Accountant\: - resource: "../src" -``` - -✅ - -### 4. Autowire the Parameter - -```php -addCompilerPass(new AutowireArrayParameterCompilerPass()); - } -} -``` - -You set up this only once, but then you can enjoy short and clear configs: - -```diff - # packages/accountant/config/services.yaml --accountant: -+parameters: - account_number: "123_secret_hash" - - services: - Project\Accountant\: - resource: "../src" -``` - -✅ - -This compiler autowires parameters by convention: - -- `%parameter_name%` => `$parameterName` - -You can [read more about it here](/blog/2018/11/05/do-you-autowire-services-in-symfony-you-can-autowire-parameters-too/). - -## Final Results - -So how does our bundle-less application looks like in the end? - -- we got rid of the `Configuration` class - no more tree fluent builds for a bunch of parameters -- we got rid of the `Extension` class - no more relative paths -- we got rid of the `Bundle` class - no more `createExtension()`, `getExtension()` typos -- we gain parameter binding/autowiring - -**We work with configs that clearly state all we parameters and services we use**. Explicit, clear, in one place. - -
- -How do you approach parameter for your packages (previously bundles) in Symfony 4 applications? diff --git a/resources/posts/2019/2019-01-10-9-steps-to-migrate-from-jekyll-to-statie.md b/resources/posts/2019/2019-01-10-9-steps-to-migrate-from-jekyll-to-statie.md deleted file mode 100644 index 3ae7d9f2c44..00000000000 --- a/resources/posts/2019/2019-01-10-9-steps-to-migrate-from-jekyll-to-statie.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -id: 176 -title: "9 Steps to Migrate From Jekyll to Statie" -perex: | - Jekyll is great a way to start static website on Github Pages. But Jekyll has one big problem - the language. - How would you add custom Twig or Latte filter to Jekyll? - - - I wanted to migrate my static websites from Jekyll to Statie. Can new `init` command make this piece of cake? And what needs to be done next? ---- - -## 1. Create Basic Statie Structure - -Statie 5.3 brings [new `init` command](/blog/2019/01/07/how-to-create-your-first-php-twig-static-website-under-2-minutes-with-statie/), that creates basic structure in `/source` directory, `statie.yml`, `.travis.yml` and metafiles. - -Before we start any moving, create a basic structure to save many copy-pasting steps: - -```bash -composer require symplify/statie -vendor/bin/statie init -``` - -Then, clean `/source` directory from generated files and... - -## 2. Move Source files to `/source` Directory - -- Jekyll has all the source code in the root. -- Statie works with `/source` directory, so the website is separated from PHP code, tests, metafiles. - -```diff --CNAME -+/source/CNAME -``` - -```diff --index.html -+/source/index.html -``` - -```diff --_data/projects.yaml -+/source/_data/projects.yaml -``` - -## 3. Move Parameters Files Under `parameters > [param name]` Sections - -**Before** - Jekyll - -```yaml -# _data/projects.yaml -- - name: Symplify - url: https://github.com/symplify/symplify -``` - -**After** - Statie - -```yaml -# source/_data/projects.yaml -parameters: - projects: - - - name: Symplify - url: https://github.com/symplify/symplify -``` - -## 4. Upgrade Absolute Links to Moved Files - -```diff --https://github.com/TomasVotruba/gophp71.org/edit/master/_data/projects.yaml -+https://github.com/TomasVotruba/gophp71.org/edit/master/source/_data/projects.yaml -``` - -## 5. Load Moved YAML Files in `statie.yml` - -```diff -+imports: -+ - { resource: "source/_data/projects.yaml" } -``` - -## 6. Remove `site.data.` and use Variables Directly - -```diff -
    -- {% for project in site.data.projects %} -+ {% for project in projects %} -
  • {{ project.name }}
  • - {% endfor %} -
-``` - -## 7. Setup Github Pages deploy in Travis - -Thanks to `vendor/bin/statie init` you have correctly configured `.travis.yml` in your repository. - -To finish deploy, you need to: - -- create `gh-pages` branch -- pick it as a source for Github Pages -- generate Github Token -- put it to Travis configuration of your repository - -How you do this? Just **follow [Statie.org documentation](https://www.statie.org/docs/github-pages)** step by step. - -## 8. Clean Metadata from Headers - -In Jekyll, it's required to have `---` section in files, even if empty. You can drop it now: - -```diff -- --- -- --- - -HTML -... -``` - -## 9. Run Project Locally - -This is what I missed the most at Jekyll page - instant feedback. We want to develop and see output instantly - is it correct or is there a bug? - -```bash -npm install -gulp -``` - -- Then open `localhost:8000` to see your generated HTML. -- Did you edit code in `/source`? → Just **refresh browser to see re-generated HTML**. - -This is at least 100 times faster than deploying to Jekyll and checking the output in the production. - -### Show me the Real Migration Code - -- [pull request on gophp71.org](https://github.com/DeprecatedPackages/gophp71.org/pull/32) -- [pull request on gomonorepo.org](https://github.com/DeprecatedPackages/gomonorepo.org/pull/7) - -
- -That's it! You can enjoy Markdown, Twig and PHP directly from your local machine and still on Github Pages. - -Happy coding! diff --git a/resources/posts/2019/2019-02-18-how-we-automated-shopsys-packages-release-from-2-days-to-1-console-command.md b/resources/posts/2019/2019-02-18-how-we-automated-shopsys-packages-release-from-2-days-to-1-console-command.md deleted file mode 100644 index 216702385c7..00000000000 --- a/resources/posts/2019/2019-02-18-how-we-automated-shopsys-packages-release-from-2-days-to-1-console-command.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -id: 187 -title: "How we Automated Shopsys Packages Release from 2 days to 1 Console Command" -perex: | - Do you **release open-source**? Do you have **monorepo**? Do you release over **10 monorepo packages at once**? - Do you still do it manually per package just to be sure? - - - Good news, it's not a single day-task, but this whole process can be automated. Let's me show how we did it in Shopsys. - ---- - -Monorepo release management has ~~few~~ many gotchas. The one that requires most of your attention I described in [Monorepo Composer Magic](/blog/2019/01/31/monorepo-composer-magic/) post. **1 missed commit or forgotten version change** in `composer.json` of your package and **you've just released a broken package**! - -I mean the ugly kind of errors you'll find out only after someone tries to install the package. Of course, you can improve this by [3-layer monorepo tests](/blog/2018/11/22/how-to-test-monorepo-in-3-layers/), but there is still a 50 % chance for human error. - -Let's get practical! - -## The *Manual* Shopsys Release Process - -[Shopsys](https://github.com/shopsys/shopsys) is an open-source e-commerce build on Symfony. I helped them with monorepo, Symfony and open-source standards setup through 2018. - -When we first looked at release todo-document, it had roughly 17 steps. After a bit deeper look we realized some steps have actually 5-10 more steps in it. In the end, we found over **40 steps in 3 various stages**. - -Just to give you the idea... - -### Before Release Stage - -- check if Travis passes for all packages -- bump package interdependency for tagged version -- bump Docker image version to tagged version -- [validate `composer.json` dependencies](/blog/2018/10/08/new-in-symplify-5-create-merge-and-split-monorepo-with-1-command/#2-validate-it) of each package -- [dump `CHANGELOG.md`](/blog/2018/06/25/let-changelog-linker-generate-changelog-for-you/) since the previous release -- check `UPGRADE.md` -- ... - -### Release Stage - -- check changelog has today's date -- push the tag (and let CI service do the split) -- ... - -### After Release Stage - -- open branch alias for `next-version-dev` -- bump package interdependency for `next-version-dev` -- bump Docker image version to `dev` -- check packages are pushed on Packagist -- ... - -

-Do you want to check them all? Just see this directory on Github. -

- -
- -Shopsys [releases new version every month](https://github.com/shopsys/shopsys/releases) and they had to do all these steps manually. Until now. Automation of this process **saves time and attention of 3 developers** (2 for code-review), that could be used for new features. - -No surprise here, that the final [pull-request](https://github.com/shopsys/shopsys/pull/623) got a bit out of hand... - - - -It **took 49 days** and **3 900 new lines** to get the PR merged. Why? Well, when you introduce simple automatization of a complex process, **people start to see how easy is to convert manual daunting work to a new PHP class that does the work for them**. So more and more ideas came. - -## From Human Check-list to Command with Workers - -To automate the process, we used MonorepoBuilder, resp. it's [release-flow feature](https://github.com/symplify/monorepobuilder#6-release-flow). - -```bash -composer require symplify/monorepo-builder -``` - -The implements a worker for each step described above. Workers are grouped by stage and ordered by priority, so the whole process is under control. - -```php -getVersionString() . ' - ' . DateTime::from('today')->format('Y-m-d') - ); - - FileSystem::write($changelogPath, $newContent); - } -} -``` - -Each step is written this way. - -

-Do you want to include stages? We did, so the worker implemented Symplify\MonorepoBuilder\Release\Contract\ReleaseWorker\StageAwareInterface. -

- -In these workers, you can trigger Travis CI with API, use Packagist API to check new version are released... sky it the limit. - -## Shopsys Release Process *Now*? - -```bash -vendor/bin/monorepo-builder release v7.0.0-beta6 --stage release-candidate - -# → review... - -vendor/bin/monorepo-builder release v7.0.0-beta6 --stage release - -# → CI work + 2nd review... - -vendor/bin/monorepo-builder release v7.0.0-beta6 --stage after-release -``` - -Complex process made simple with PHP! ✅ - -
- -Btw, do you know how Symplify 14-package monorepo release process looks like? - -```bash -vendor/bin/monorepo-builder release v5.4.1 -``` - -Just with bare [MonorepoBuilder](https://github.com/Symplify/MonorepoBuilder) install. - -
- -**How does your package release process look like?** diff --git a/resources/posts/2019/2019-05-13-hidden-gems-of-php-packages-psalm-fixing-your-code.md b/resources/posts/2019/2019-05-13-hidden-gems-of-php-packages-psalm-fixing-your-code.md deleted file mode 100644 index b84058bdc2c..00000000000 --- a/resources/posts/2019/2019-05-13-hidden-gems-of-php-packages-psalm-fixing-your-code.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -id: 211 -title: "Hidden Gems of PHP Packages: Psalm Fixing Your Code" -perex: | - Psalm is a static analyzer of PHP code originated at Vimeo and developed by [Muglug](https://github.com/muglug). It can analyze your code for incorrect type declarations or unused code. - - - But did you know it can automatically fix these issues? - -updated_since: "June 2019" -updated_message: "A new option `--issues=all` is added in Psalm 3.3.1" ---- - -I did not and I was surprised. Psalm added this feature long time ago in March 2018 (as [reported on Reddit](https://www.reddit.com/r/PHP/comments/84xrgy/fixing_code_that_aint_broken_vimeo_engineering))! I was also surprised because static analyzer is read-only tools - "here is error → fix it yourself manually" - -## Static Analyzers Evolving to Code Fixers - -**But it's not an unexpected development.** As I wrote in *[Brief History of Tools Watching and Changing Your PHP Code](/blog/2018/10/22/brief-history-of-tools-watching-and-changing-your-php-code/)*, coding standard tools were *read-only* too and you had to change all the spaces manually. That was so annoying with large code-bases and many programmers didn't adopt it because it added the extra work on a daily basis, instead of saving time. - -In response to this pressure, they **had to become more useful by working for the programmer**. PHP CS Fixer fixed code from the very first commit and PHP_CodeSniffer added this feature in response. - -We can assume PHPStan and Phan will follow Psalm path since human-fixing is hard to scale and will become annoying in time compared to automated instant upgrades. - -
- -So what Psalm can do for you? - -## 1. Missing Type Declaration? - -Do you know that feeling when PHPStan reports "Method X is returning an int, but should be a string" in 10 000 places? Yes, you can use [Baseliner](/blog/2019/04/22/hidden-gems-of-php-packages-srab/) to ignore them and check only new code, **but that only postpones the problem**. One day there still will be 3-4 days of full-time boring work ahead of you. - -```php -/** - * @return int - */ -function foo() -{ - return 'hello'; -} -``` - -That's what Psalm fixes for you and even adds the return type declaration: - -```diff --/** -- * @return int -- */ --function foo() -+function foo(): string - { - return 'hello'; - } -``` - -### How to Run it? - -Just use binary with `--alter` (that says "fix" this) + the `--issues` option: - -```bash -vendor/bin/psalm src --alter --issues=MissingReturnType -vendor/bin/psalm src --alter --issues=MissingClosureReturnType -vendor/bin/psalm src --alter --issues=InvalidReturnType -vendor/bin/psalm src --alter --issues=InvalidNullableReturnType -``` - -The docs doesn't say if they stack together, but I'd assume so by the plural in "issues": - -```bash -vendor/bin/psalm src --alter --issues=MissingReturnType,MissingClosureReturnType,InvalidReturnType,InvalidNullableReturnType -``` - -## 2. Falseable Strings? - -Though the same kind of *detect type → complete it* logic, this example is really nice: - -```php -function foo(): string { - return rand(0, 1) ? 'hello' : false; -} -``` - -↓ - -``` -/** - * @return string|false - */ -function foo() { - return rand(0, 1) ? 'hello' : false; -} -``` - -## 3. Unused Property or Method? - -I wish this would be run before each code-review. Imagine you decouple a method during refactoring and stop using one of the existing methods in the same class. A dead method is born. **A dead method that you need to maintain, test and upgrade to new version of PHP or your framework.** - -Not anymore. - -```diff - class A { -- private function foo() : void {} -- protected function bar() : void {} -- public function baz() : void {} - } - - new A(); -``` - -Same goes for properties: - -```diff - class A { -- /** @var string */ -- public $foo; - -- /** @var string */ -- protected $bar; - } - - new A(); -``` - -### How to Run it? - -```bash -vendor/bin/psalm src --alter --issues=UnusedMethod -vendor/bin/psalm src --alter --issues=PossiblyUnusedMethod -vendor/bin/psalm src --alter --issues=UnusedProperty -vendor/bin/psalm src --alter --issues=PossiblyUnusedProperty -``` - -## 4. Undefined Variable? - -How do you like this code? - -```php -if (rand(0, 1)) { - $a = 5; -} -echo $a; -``` - -Ups, `$a` is not defined (sometimes). - -PHPStorm would tell you what's wrong with this code if you'd be writing this code. But again, it adds you extra manual work and doesn't ~~check~~ fix the rest of your huge code base from your CI. - -Psalm can: - -```diff -+$a = null; - if (rand(0, 1)) { - $a = 5; - } - echo $a; -``` - -If you're into static analysis, you know it's very hard to examine this flow of control and moreover add the variable not just the beginning of file or method, but to the right place where you or I would add it. - -Cudos to [Mathew](https://github.com/muglug)! 👍 - -### How to Run it? - -```bash -vendor/bin/psalm src --alter --issues=PossiblyUndefinedVariable -``` - -## Check all 13 of them - -At the time of writing this post, there is 13 issue alters now. I believe we can expect up to 100 more in next year or two. - -You can run them all like this: - -```bash -vendor/bin/psalm src --alter --issues=all -``` - -**Read about them and about extra options like `--php-version`, `--dry-run` or `--safe-types` in [very beautiful and short documentation](https://psalm.dev/docs/fixing_code)**. - -## Try it, Even if you use PHPStan - -Personally, I use PHPStan because I'm not good with XML. But even if I should **install Psalm just to complete type-declarations and remove dead code**, it's worth the 10 minutes time to set it up. Give a try, it's huge time saver the bigger code base you have. - -You don't have to take my word for it: - - - -
- -This is the future = PHP tools working for us - enjoy it :) - -

- -Happy coding! diff --git a/resources/posts/2019/2019-09-09-how-we-upgraded-pehapkari-cz-from-symfony-4-to-5-in-25-days.md b/resources/posts/2019/2019-09-09-how-we-upgraded-pehapkari-cz-from-symfony-4-to-5-in-25-days.md deleted file mode 100644 index f3c352a316d..00000000000 --- a/resources/posts/2019/2019-09-09-how-we-upgraded-pehapkari-cz-from-symfony-4-to-5-in-25-days.md +++ /dev/null @@ -1,445 +0,0 @@ ---- -id: 262 -title: "How we Upgraded Pehapkari.cz from Symfony 4 to 5 in 25 days" -perex: | - A month ago, Symfony 5 has been released. Upgrading of such a small web as our community website **must be easy**, right? - - Well, that's what we thought. **Were we right or wrong?** - - -updated_since: "November 2020" -updated_message: | - Switch from deprecated `--set` option to `rector.php` config. ---- - -This post is based on the real problems we faced when we upgraded our website. **It is full of experience** with pieces of explanation, real code snippets in diffs, painful frustration of Symfony ecosystem and bright light at the end of the tunnel. - -
- -Are you ready? Let's dive in ↓ - -
- -## 1. Easy picks - -### Twig 2 → 3 - -Before, you could connect `for` with `if` like this: - -```twig -{% for post in posts if post.isPublic() %} - {{ post.title }} -{% endfor %} -``` - -Now [the `filter`](https://twig.symfony.com/doc/3.x/filters/filter.html) has to be used: - -```twig -{% for post in posts|filter(post => post.isPublic()) %} - {{ post.title }} -{% endfor %} -``` - -*Thanks [Patrik for the tip](https://www.reddit.com/r/PHP/comments/ef2nit/how_we_upgraded_pehapkaricz_from_symfony_4_to_5/fbzyhsl)* - -## 2. Rector Helps You with PHP - -Do you want to know, what is needed for the upgrade to Symfony 5? [Just read upgrade notes](https://github.com/symfony/symfony/blob/5.0/UPGRADE-5.0.md) in Symfony repository. - -1. For PHP stuff, use [Rector](https://github.com/rectorphp/rector): - -```bash -# install Rector -composer require rector/rector --dev - -# or in case of conflicts -composer require rector/rector-prefixed --dev -``` - -Rector has minimal sets, meaning each minor version is standalone and independent. -What does that mean? For upgrading from Symfony 4 to 5, you need to **run all the minor version sets**, one by one. - -2. Update `rector.php` config - -```php -use Rector\Symfony\Set\SymfonySetList; -use Rector\Config\RectorConfig; - -return function (RectorConfig $rectorConfig): void { - $rectorConfig->import(SymfonySetList::SYMFONY_41); - - // take it 1 set at a time, so next set works with output of the previous set; I do 1 set per pull-request - // $rectorConfig->import(SymfonySetList::SYMFONY_42); - // $rectorConfig->import(SymfonySetList::SYMFONY_43); - // $rectorConfig->import(SymfonySetList::SYMFONY_44); - // $rectorConfig->import(SymfonySetList::SYMFONY_50); -}; -``` - -3. Run Rector - -```bash -vendor/bin/rector process app src tests -``` - -Verify, check that CI passes and then continue with next Symfony minor version. - -## 3. Update `composer.json` before `composer update` - -### Easy Admin Bundle - -```diff - { - "require": { -- "alterphp/easyadmin-extension-bundle": "^2.1", -+ "alterphp/easyadmin-extension-bundle": "^3.0", - } -} -``` - -### Doctrine - -```diff - { - "require": { -- "doctrine/cache": "^1.8", -+ "doctrine/cache": "^1.10", -- "doctrine/doctrine-bundle": "^1.11", -+ "doctrine/doctrine-bundle": "^2.0", -- "doctrine/orm": "^2.6", -+ "doctrine/orm": "^2.7", - } - } -``` - -### Doctrine Behaviors - -```diff - { - "require": { -- "stof/doctrine-extensions-bundle": "^1.3", -- "knplabs/doctrine-behaviors": "^1.6" -+ "knplabs/doctrine-behaviors": "^2.0" - } - } -``` - -### Sentry - -```diff - { - "require": { -- "sentry/sentry-symfony": "^3.2", -+ "sentry/sentry-symfony": "^3.4", - } - } -``` - -### Twig Extensions were Removed - -```diff - { - "require": { -- "twig/extensions": "^1.5" - } - } -``` - -This might be scary at first, depends on how many of those functions have you used. - -Look at the [README on Github](https://github.com/twigphp/Twig-extensions) to find out more: - -
- -
- -## 4. Symfony Packages in `composer.json` - -Do you use Flex and config `*` version? - -```json -{ - "require": { - "symfony/console": "*", - "symfony/event-disptacher": "*" - }, - "extra": { - "symfony": { - "require": "^4.4" - } - } -} -``` - -Not sure why, but in some cases, it failed and **blocked from the upgrading**. **I had to switch to explicit version per package, to resolve it:** - -```diff - { - "require": { -- "symfony/console": "*", -+ "symfony/console": "^4.4", -- "symfony/event-disptacher": "*" -+ "symfony/event-disptacher": "^4.4" -- }, -+ } -- "extra": { -- "symfony": { -- "require": "^4.4" -- } -- } - } -`````` - -Then switch to Symfony 5: - -```diff --"symfony/asset": "^4.4", -+"symfony/asset": "^5.0", --"symfony/console": "^4.4", -+"symfony/console": "^5.0", - -// etc. -``` - -But some packages are released out of [monorepo cycle](/blog/2019/10/28/all-you-always-wanted-to-know-about-monorepo-but-were-afraid-to-ask/): - -```diff --"symfony/maker-bundle": "^1.14", -+"symfony/maker-bundle": "^1.13", -``` - -All right, now you run... - -```bash -composer update -``` - -...and get new packages with Symfony 5... or probably a lot of composer version conflicts. - -### Symfony Packages WTFs in - -In Symfony 5, some packages were **removed**: - -```diff --"symfony/error-renderer": "^4.4", -``` - -```diff --"symfony/web-server-bundle": "^4.4", -``` - -
- -Some packages were **replaced by new ones**: - -```diff --"symfony/error-debug": "^4.4", -+"symfony/error-handler": "^5.0", -``` - -
- -And some package **were split into more smaller ones**: - -```diff --"symfony/security": "^4.4", -+"symfony/security-core": "^5.0", -+"symfony/security-http": "^5.0", -+"symfony/security-csrf": "^5.0", -+"symfony/security-guard": "^5.0", -``` - -## 5. Rector, ECS, and PHPStan - -These were production dependencies, but what about dev ones? -Both have the same rules - they need to allow Symfony 5 installation. - -The safest way is to use [prefixed versions](https://github.com/rectorphp/rector/issues/177), which don't care about a Symfony version: - -```diff --"phpstan/phpstan": "^0.11", -+"phpstan/phpstan": "^0.12", --"rector/rector": "^0.5", -+"rector/rector-prefixed": "^0.6", -``` - -[Easy Coding Standard](https://github.com/symplify/easy-coding-standard): - -```diff --"symplify/easy-coding-standard": "^0.11", -+"symplify/easy-coding-standard": "^0.12", -``` - -
- -Update your `composer.json` to include a package that you need. - -Then run: - -```bash -composer update -``` - -Still conflicts? - -## 6. And The Biggest Symfony Upgrade Blocker is... - -If you don't do open-source, you probably don't use the `git tag` feature. It seems that the tagging of a package is a very difficult process. Even packages with million downloads/month had the latest 15 months ago. - -### What are Tags For? - -Let's say you want to use `symplify/easy-coding-standard` that supports Symfony 5. Here is the deal: - -- the latest `symplify/easy-coding-standard` version 6 doesn't support it -- `symplify/easy-coding-standard` dev-master (~= what you see on GitHub) supports it -- but it's not tagged yet and composer forbids to install dev version; e.g. [sentry-symfony](https://github.com/getsentry/sentry-symfony) at time of writing this post -- so you'd have to require its dev version and force composer to install it - -```json -{ - "require-dev": { - "symplify/easy-coding-standard": "dev-master" - }, - "prefer-stable": true, - "minimum-stability": "dev" -} -``` - -- it's a hackish solution, but it *somehow* works - -Now imagine one of your package you require requires some other package, that requires another package, that doesn't allow Symfony 5 in tagged version, but in `master`. Well, you've just finished. - -That's why it's very important to know to tag a package regularly: - -```bash -git tag v2.0.0 -git push --tags -``` - -That's all! Still, many packages support Symfony 5 in the master but are not tagged yet... to be true, not once for the last 2 years. Why? **The human factor**, maintainers are afraid of negative feedback, of back-compatibility breaks, lack of test coverage takes their confidence, etc. - -## Tagging Cancer of PHP Ecosystem - -These packages block Symfony 5 upgrade for months: - -- [stof/gedmo extension](https://github.com/stof/StofDoctrineExtensionsBundle/releases) (last release in 2017) -- [knplabs/doctrine-behaviors](https://github.com/KnpLabs/DoctrineBehaviors/releases) (last stable release in 2018) -- [behat/transliterator](https://github.com/Behat/Transliterator/releases) (last release in 2017) - this [comment sums it up very nicely](https://github.com/Behat/Transliterator/pull/29#issuecomment-567873541) - -
- -
- -### United We Stand - -This will be resolved in the future by an open-source monorepo approach, but we're not there yet. - -In the meantime, please **complain at issues**, ask for help and **offer to become maintainer** until it changes (or until somebody forks it and tags the fork). - -One good example for all - I complained and offered help at `knplabs/doctrine-behaviors`, got maintainer rights in 3 hours and [made + merged 30 pull-request in the last month](https://github.com/KnpLabs/DoctrineBehaviors/pulse/monthly). - -You see, it works :) - -## 7. Still Conflicts? - -Ok, so you have the right version of packages, everything is stable and allows Symfony 5. Yet still, the composer says "conflicts, cannot install x/y". - -To my surprise, the composer is very bad at solving complex conflicts. Composer **reports false positive and blocks your install**, because of installing packages in `/vendor` or overly strict `composer.lock`. I spent 30-60 minutes trying to figure out what the heck is conflicting on every Symfony training I had in the last 2 months. Now I'm able to do it in 3 minutes. - -**How?** - -- remove `/vendor` -- remove `composer.lock` -- run `composer update` - -It works so well I do it more often than resolving conflicts manually. - - -## 8. Cleanup `bundles.php` - -- Doctrine Cache was only [released for Symfony 4.4 and is not supported for any further version](https://github.com/doctrine/DoctrineCacheBundle/releases/tag/1.4.0). - -```diff - return [ -- Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], - ]; -``` - -Switch the dead gedmo/stof doctrine extensions for the maintained [KnpLabs/DoctrineBehaviors](https://github.com/KnpLabs/DoctrineBehaviors). I'll write a standalone post about this migration, once a stable version is out (check me, pls :)). - -```diff - return [ -- Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], - ]; -``` - -We also had some troubles with Switfmailer Bundle: - -```diff - return [ -- Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], - ] -``` - -The [Mailer component](https://symfony.com/blog/new-in-symfony-4-3-mailer-component) will take over Swiftmailer in the future, so this is just a start. - -## 9. Clear `config/packages` - -```diff - # config/packages/framework.yaml - framework: - ... -- templating: -- engines: ["twig"] -``` - -Don't forget to remove all extension configs. In our case it was: - -```diff --config/packages/stof_doctrine_extensions.yaml --config/packages/swiftmailer.yaml --config/packages/dev/swiftmailer.yaml --config/packages/test/swiftmailer.yaml --config/packages/twig_extensions.yaml --config/routes/dev/twig.yaml -``` - -Small update of the EasyAdmin bundle: - -```diff - # config/routes/easy_admin.yaml - easy_admin_bundle: -- resource: '@EasyAdminBundle/Controller/AdminController.php' -+ resource: '@EasyAdminBundle/Controller/EasyAdminController.php' -``` - -
- And that's all folks!
- Got any questions? -
- -
- All the know-how is taken from practical pull-request, that was under strict Travis control: - -
- -
- - Feel free to explore it, ask, read comments or share your problems. -
- - - Check the PR on Github - -
-
- -## Have we Missed Something? - -Of course, we did! Every application has a different set of *blocking* dependencies and different sets of used Symfony features that might have changed. - -Share your issues in comments or edit this post on Github to make list complete! - -
- -Happy coding! diff --git a/resources/posts/2019/2019-12-02-how-to-box-symfony-app-to-phar-without-killing-yourself.md b/resources/posts/2019/2019-12-02-how-to-box-symfony-app-to-phar-without-killing-yourself.md deleted file mode 100644 index ce8f5181bf5..00000000000 --- a/resources/posts/2019/2019-12-02-how-to-box-symfony-app-to-phar-without-killing-yourself.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -id: 228 -title: "How to Box Symfony App to PHAR without Killing Yourself" -perex: | - Do you have a Symfony Application like Composer and you want to ship it as a PHAR? - Composer is actually pretty simple - just see the [`Compiler`](https://github.com/composer/composer/blob/master/src/Composer/Compiler.php) class. - - - **But what if** you use Symfony Dependency Injection with PSR-4 **autodiscovery** like [Rector](https://github.com/rectorphp/rector) does? Well, better be ready for **nasty traps**. ---- - -*Note: all these tips take 5 minutes to apply (in total), but took us ~6 hours to discover. I'd like to thank Jan Linhart from Mautic, Kerrial Becket Newham and Ivan Kvasnica for cooperation that made [this happen](https://github.com/rectorphp/rector/pull/2373).* - -Rector [needs prefixed PHAR](https://github.com/rectorphp/rector/issues/177) for the same reasons PHPStan does. - -Let's say your `composer.json` looks like this: - -```json -{ - "require": { - "symfony/console": "2.8" - } -} -``` - -If you want to install Rector: - -```bash -composer require rector/rector --dev -``` - -You'll end up with an error: - -```bash -rector/rector cannot be installed, because it requires symfony/* ^3.4|^4.4... but you have 2.8 -``` - -This leads to many issues reported on Github, mostly [grouped around this one](https://github.com/rectorphp/rector/issues/177). - -## PHP Version Conflicts - -If you have PHP 5.6, you'll get a different error: - -```bash -rector/rector needs at least PHP 7.2, you have PHP 5.6 -``` - -That's where Docker becomes useful. Yet, it still **doesn't solve the Symfony 2.8 in your project vs Symfony 4.4 in Rector project issue**. - -That's why **prefixed `rector.phar` is needed**. With such a file, you don't care about Rector's dependencies, you just use it. - -## How Does "Scoping" Work? - -Basically any `Symfony\Component\Console\Command` becomes `UniquePrefix\Symfony\Component\Console\Command`. That way there will never be conflicts between your code without prefix and unique Rector code. - - -## Box + Scope Industry Standard - -To make it happen, we **don't need to re-invent the wheel**. There are 2 amazing tools maintained and developed by [Théo Fidry](https://github.com/theofidry) (thank you!): - -- [box](https://github.com/humbug/box) - a tool that creates PHAR (~= PHP zip) from an input directory -- [php-scoper](https://github.com/humbug/php-scoper) - box *plugin* that adds namespace prefix to all the files - -It takes around 10 seconds to scope + wraps 5 000 files of Rector to `rector.phar`. **This speed is amazing.** - -### Nobody Ever used Symfony in PHAR Before - -These 2 tools work very well for PHP-based *manual* containers like PHPStan has. But fails for Symfony autodiscovery that uses globs. It's not the fault of these tools, but rather Symfony, because nobody ever tested it to compiled PHAR :). - -**Where and how to overcome it?** There are 4 steps you need to watch out for: - -## 1. From `excluded` files to Globs - -If you have following config: - -```yaml -services: - Rector\TypeDeclaration\: - resource: '../src' - exclude: - - '../src/ValueObject/SpecificFile.php' -``` - -You'll end up with an error: - -```bash -Directory "../src/ValueObject/SpecificFile.php" was not found. -``` - -Where does it come from? It's an error from [Symfony/Finder](https://github.com/symfony/symfony/blob/c62bb82e27974ef4e389da523f0de451b6632266/src/Symfony/Component/Finder/Finder.php#L589). - -But how did Symfony got there? Well, the Symfony takes missing files from ["excluded" as directory](https://github.com/symfony/symfony/blob/c62bb82e27974ef4e389da523f0de451b6632266/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php#L162) and the rest is history. - -My super random guess is for missing local `phar://` prefix. - - -### How to Fix it? - -Just change the relative path to each file to glob (`*`) and move your files there: - -```diff - services: - Rector\TypeDeclaration\: - resource: '../src' - exclude: -- - '../src/ValueObject/SpecificFile.php' -+ - '../src/ValueObject/*' -``` - -### Positive Side-Effect - -In the end, it was architecture improvement, as we had to move files to a generic directory, that clearly states it's not a service - here `ValueObject`. - -## 2. Symfony Autodiscovery Slash Fail - -This one give me an headache, but is simple to fix: - -```diff - services: - Rector\TypeDeclaration\: -- resource: '../src/' -+ resource: '../src' -``` - -## 3. SHA1 cannot be Verified... - -This one is not strictly related to Symfony, but it happened while we shipped `box.phar`: - -```bash -Error: Fatal error: Uncaught PharException: phar "compiler/build/box.phar" SHA1 -the signature could not be verified: broken signature in ... -``` - -What the `box.phar` worked locally but doesn't work on Travis? - -I re-downloaded files many times and it worked in other CI. WTF? - -Is that corrupted version of `box.phar`? I tried version before/after, still the same error. - -
- -2 hours later... - -
- -Damn. Spaces? Line-ending? **Yes!** - -The solution is to remove this from `.gitattributes`: - -```diff --# Set default behavior, in case of users, don't have core.autocrlf set. --* text=auto --* text eol=lf -``` - -Because [it changed line-endings](https://stackoverflow.com/questions/24763948/git-warning-lf-will-be-replaced-by-crlf) in the `box.phar` on commit and thus made it valid locally, but broken remotely on Travis CI. - -## 4. Don't do Multiple `bin` Files - -Rector had multiple bin files, just to split the complexity: - -- `bin/rector` that included - - `bin/autoload.php` - - `bin/container.php` - - -The Box takes only the file in `compser.json` > `bin` section, so the latter 2 were missed. I tried to change configuration many times, but it mostly failed on malformed paths. - - -### How to solve it? - -Now we have just **single file**: - -- `bin/rector` - -With use strict typed classes written on the bottom of the file and use them in the same file. Also, nice side effects as we moved from many-functions to few classes. - -
- -Do you want to know more about Box + Scoper automated Travis CI deploy in practice? - - - Check this PR on Rector - - -
- -Happy coding!