|
| 1 | +# Extension Points |
| 2 | + |
| 3 | +Proposed by: @norberttech |
| 4 | +Date: 2025-01-09 |
| 5 | + |
| 6 | +## Context |
| 7 | +--- |
| 8 | + |
| 9 | +Flow is a framework, and as all frameworks, it should provide extension points for developers to hook into the framework and extend its functionality. |
| 10 | + |
| 11 | +How to find an extension point? |
| 12 | + |
| 13 | +Easy, look for interfaces! For example take a look at `\Flow\ETL\Cache` which is |
| 14 | +an interface with few implementations. Thanks to the interface, you can easily |
| 15 | +create your own implementation and use it in the framework. |
| 16 | + |
| 17 | +Most of the extension points exposed through `\Flow\ETL\Config` where you can |
| 18 | +replace a default implementations of various interfaces with those that better |
| 19 | +fit your needs. |
| 20 | + |
| 21 | +Those extension points are there by design and should follow the backward compatibility policy. |
| 22 | + |
| 23 | +Are there any other extension points that we should consider? |
| 24 | + |
| 25 | +Yes... Unfortunately, each class that is not marked final and that is being somehow |
| 26 | +injected into the DataFrame must be considered as an extension point. |
| 27 | + |
| 28 | +Why? Because nothing prevents developers from extending those classes and injecting |
| 29 | +their custom version into the DataFrame. |
| 30 | + |
| 31 | +Those extension points can't be covered by our backward compatibility policy. |
| 32 | + |
| 33 | +## Decision |
| 34 | +--- |
| 35 | + |
| 36 | +**Closed by default** |
| 37 | + |
| 38 | +All classes should be marked as `final` and whenever we need to expose an extension point, |
| 39 | +we should do it through an interface. |
| 40 | + |
| 41 | +**Always possible to open later** |
| 42 | + |
| 43 | +In a justifiable cases, we can expose extension points through abstract classes or |
| 44 | +simply allowing people to extend the class. Those must always be approved by one of |
| 45 | +the core contributors. |
| 46 | + |
| 47 | +**No guarantee and support for workarounds** |
| 48 | + |
| 49 | +Developers can still use reflections or dedicated tools to "unfinalize" Flow classes, |
| 50 | +but we do not guarantee backward compatibility for those cases and everyone should |
| 51 | +do it at their own risk. As a Flow PHP maintainers, we also reserve the right to |
| 52 | +not provide any help or support for those cases. |
| 53 | + |
| 54 | +## Pros & Cons |
| 55 | +--- |
| 56 | + |
| 57 | +- zero risk of breaking accidentally backward compatibility promise |
| 58 | +- more predictable behavior |
| 59 | +- reduced cost of maintaining backward compatibility |
| 60 | +- easier to find an actual extension points |
| 61 | +- impossible to mock classes that aren’t marked as `final` in tests (which is a good thing, users shouldn’t mock Flow classes in their test suites) |
| 62 | + |
| 63 | +## Alternatives Considered (optional) |
| 64 | +--- |
| 65 | + |
| 66 | +Alternatively we could take similar approach to Symfony and mark classes as `final` |
| 67 | +[when it makes sense](https://github.com/symfony/symfony/issues/15233#issuecomment-1566682054) |
| 68 | +but this approach is too easy to get abused. |
| 69 | + |
| 70 | +Plus, changing non-final class into a final one is much bigger BC break |
| 71 | +then the other way around. |
| 72 | + |
| 73 | +## Links and References (optional) |
| 74 | +--- |
| 75 | + |
| 76 | +- [When to declare classes final](https://ocramius.github.io/blog/when-to-declare-classes-final/) |
| 77 | +- [Final Classes by default, why?](https://matthiasnoback.nl/2018/09/final-classes-by-default-why/) |
0 commit comments