├── Classes
├── Cache
│ ├── CacheFactory.php
│ └── ClassCacheManager.php
├── Command
│ └── ClearCommand.php
├── Composer
│ ├── ClassComposer.php
│ └── Generator
│ │ ├── ClassGenerator.php
│ │ ├── ClassMethodGenerator.php
│ │ ├── ConstantGenerator.php
│ │ ├── ConstructorGenerator.php
│ │ ├── FileCommentGenerator.php
│ │ ├── GeneratorInterface.php
│ │ ├── InitializeObjectGenerator.php
│ │ ├── NamespaceGenerator.php
│ │ ├── PropertyGenerator.php
│ │ ├── TraitGenerator.php
│ │ └── UseGenerator.php
├── Configuration
│ └── ClassRegister.php
├── DependencyInjection
│ └── RegisterExtenderPass.php
├── Event
│ └── RegisterAutoloaderEvent.php
├── Exception
│ ├── BaseFileNotFoundException.php
│ └── ExtendingFileNotFoundException.php
├── Hooks
│ └── ClearCacheHook.php
├── Loader
│ └── ClassLoader.php
└── Parser
│ ├── ClassParser.php
│ ├── FileSegments.php
│ └── Visitor
│ ├── AbstractVisitor.php
│ ├── ClassMethodVisitor.php
│ ├── ClassVisitor.php
│ ├── ConstantVisitor.php
│ ├── ConstructorVisitor.php
│ ├── InitializeObjectVisitor.php
│ ├── NamespaceVisitor.php
│ ├── PropertyVisitor.php
│ ├── TraitVisitor.php
│ └── UseVisitor.php
├── Configuration
├── Services.php
└── Services.yaml
├── Documentation
├── Breaking
│ └── Index.rst
├── Configuration
│ └── Index.rst
├── Includes.rst.txt
├── Index.rst
├── Installation
│ └── Index.rst
├── Introduction
│ ├── Images
│ │ ├── base.png
│ │ ├── extend.png
│ │ └── merged.png
│ └── Index.rst
├── Sitemap.rst
├── Testing
│ ├── Images
│ │ └── phpstorm_configuration.png
│ └── Index.rst
└── guides.xml
├── LICENSE.txt
├── README.md
├── Resources
└── Public
│ └── Icons
│ └── Extension.svg
├── composer.json
├── ext_emconf.php
└── ext_localconf.php
/Classes/Cache/CacheFactory.php:
--------------------------------------------------------------------------------
1 | >
29 | */
30 | protected static array $configuration = [
31 | 'frontend' => PhpFrontend::class,
32 | 'backend' => FileBackend::class,
33 | 'groups' => [
34 | 'all',
35 | 'system',
36 | ],
37 | 'options' => [
38 | 'defaultLifetime' => AbstractBackend::UNLIMITED_LIFETIME,
39 | ],
40 | ];
41 |
42 | public function createCache(string $identifier): ?FrontendInterface
43 | {
44 | self::addClassCacheConfigToGlobalTypo3ConfVars();
45 | try {
46 | $cache = Bootstrap::createCache($identifier);
47 | } catch (\Exception) {
48 | $cache = null;
49 | }
50 | return $cache;
51 | }
52 |
53 | public static function addClassCacheConfigToGlobalTypo3ConfVars(): void
54 | {
55 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extender'] = static::$configuration;
56 | $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']['extender'] =
57 | ClearCacheHook::class . '->clearCachePostProc';
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Classes/Cache/ClassCacheManager.php:
--------------------------------------------------------------------------------
1 | getBaseClassFileSegments($className),
47 | ...$this->getExtendingClassesFileSegments($className),
48 | ];
49 |
50 | $code = $this->getMergedFileCode($fileSegments);
51 | $this->addFileToCache($cacheEntryIdentifier, $code);
52 | }
53 |
54 | protected function getBaseClassFileSegments(string $className): FileSegments
55 | {
56 | return $this->getFileSegments($className, true, BaseFileNotFoundException::class);
57 | }
58 |
59 | /**
60 | * @return FileSegments[]
61 | */
62 | protected function getExtendingClassesFileSegments(string $baseClassName): array
63 | {
64 | $filesSegments = [];
65 |
66 | foreach ($this->classRegister->getExtendingClasses($baseClassName) as $className) {
67 | $filesSegments[] = $this->getFileSegments($className, false, ExtendingFileNotFoundException::class);
68 | }
69 |
70 | return $filesSegments;
71 | }
72 |
73 | protected function getFileSegments(string $className, bool $baseClass, string $exceptionClass): FileSegments
74 | {
75 | $type = $baseClass ? 'base' : 'extend';
76 | $filePath = $this->classLoader->findFile($className);
77 | $filePath = realpath($filePath);
78 |
79 | if ($filePath === false) {
80 | throw new $exceptionClass(
81 | 'Composer did not find the file path for ' . $type . ' class "' . $className . '"'
82 | );
83 | }
84 |
85 | if (!is_file($filePath)) {
86 | throw new $exceptionClass(
87 | 'File "' . $filePath . '" for ' . $type . ' class "' . $className . '" does not exist'
88 | );
89 | }
90 |
91 | $fileSegments = $this->classParser->getFileSegments($filePath);
92 | $fileSegments->setBaseClass($baseClass);
93 |
94 | return $fileSegments;
95 | }
96 |
97 | /**
98 | * @param FileSegments[] $fileSegments
99 | */
100 | protected function getMergedFileCode(array $fileSegments): string
101 | {
102 | return $this->classComposer->composeMergedFileCode($fileSegments);
103 | }
104 |
105 | protected function addFileToCache(string $cacheEntryIdentifier, string $code): void
106 | {
107 | try {
108 | $this->classCache->set($cacheEntryIdentifier, $code);
109 | } catch (\Exception) {
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Classes/Command/ClearCommand.php:
--------------------------------------------------------------------------------
1 | cacheFactory->createCache('extender')->flush();
38 | $output->writeln('Cache cleared');
39 | } catch (\Exception $e) {
40 | $output->writeln('' . $e->getMessage() . '');
41 | $result = self::FAILURE;
42 | }
43 | return $result;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Classes/Composer/ClassComposer.php:
--------------------------------------------------------------------------------
1 | generators as $generator) {
49 | $statements = $this->addFileStatement($statements, $fileSegments, $generator);
50 | }
51 |
52 | $prettyPrinter = new PrettyPrinter();
53 | $fileCode = $prettyPrinter->prettyPrintFile($statements);
54 |
55 | return str_replace('generate($statements, $fileSegments);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/ClassGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
34 | $class = $this->getClass($fileSegments);
35 |
36 | if ($class) {
37 | foreach ($fileSegments as $fileSegment) {
38 | if ($fileSegment->isBaseClass()) {
39 | continue;
40 | }
41 | $class->implements = $this->getUniqueImplements($fileSegments);
42 | }
43 | $namespace->stmts[] = $class;
44 | }
45 |
46 | return $statements;
47 | }
48 |
49 | /**
50 | * @param FileSegments[] $fileSegments
51 | * @return Name[]
52 | */
53 | protected function getUniqueImplements(array $fileSegments): array
54 | {
55 | $implements = [];
56 | foreach ($fileSegments as $fileSegment) {
57 | /** @var Name $currentImplement */
58 | foreach ($fileSegment->getClass()->implements as $currentImplement) {
59 | if (isset($implements[(string)$currentImplement])) {
60 | continue;
61 | }
62 | $implements[(string)$currentImplement] = $currentImplement;
63 | }
64 | }
65 | return array_values($implements);
66 | }
67 |
68 | /**
69 | * @param Node[] $statements
70 | */
71 | protected function getNamespace(array $statements): ?Namespace_
72 | {
73 | $namespace = null;
74 | foreach ($statements as $statement) {
75 | if ($statement instanceof Namespace_) {
76 | $namespace = $statement;
77 | break;
78 | }
79 | }
80 | return $namespace;
81 | }
82 |
83 | /**
84 | * @param FileSegments[] $fileSegments
85 | */
86 | protected function getClass(array $fileSegments): ?Class_
87 | {
88 | $class = null;
89 | foreach ($fileSegments as $fileSegment) {
90 | if ($fileSegment->isBaseClass() && $fileSegment->getClass()) {
91 | $class = $fileSegment->getClass();
92 | break;
93 | }
94 | }
95 | return $class;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/ClassMethodGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
34 | $class = $this->getClass($namespace);
35 |
36 | if ($class) {
37 | $class->stmts = [...$class->stmts, ...$this->getUniqueClassMethods($fileSegments)];
38 | }
39 |
40 | return $statements;
41 | }
42 |
43 | /**
44 | * @param FileSegments[] $fileSegments
45 | * @return ClassMethod[]
46 | */
47 | protected function getUniqueClassMethods(array $fileSegments): array
48 | {
49 | $classMethods = [];
50 | foreach ($fileSegments as $fileSegment) {
51 | foreach ($fileSegment->getMethods() as $currentMethod) {
52 | if (isset($classMethods[(string)$currentMethod->name])) {
53 | continue;
54 | }
55 | $classMethods[(string)$currentMethod->name] = $currentMethod;
56 | }
57 | }
58 | return array_values($classMethods);
59 | }
60 |
61 | /**
62 | * @param Node[] $statements
63 | */
64 | protected function getNamespace(array $statements): ?Namespace_
65 | {
66 | $namespace = null;
67 | foreach ($statements as $statement) {
68 | if ($statement instanceof Namespace_) {
69 | $namespace = $statement;
70 | break;
71 | }
72 | }
73 | return $namespace;
74 | }
75 |
76 | protected function getClass(Namespace_ $namespace): ?Class_
77 | {
78 | /** @var ?Class_ $class */
79 | $class = null;
80 | foreach ($namespace->stmts as $node) {
81 | if ($node instanceof Class_) {
82 | $class = $node;
83 | break;
84 | }
85 | }
86 | return $class;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/ConstantGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
34 | $class = $this->getClass($namespace);
35 |
36 | if ($class) {
37 | $class->stmts = [...$class->stmts, ...$this->getUniqueClassConstants($fileSegments)];
38 | }
39 |
40 | return $statements;
41 | }
42 |
43 | /**
44 | * @param FileSegments[] $fileSegments
45 | * @return ClassConst[]
46 | */
47 | protected function getUniqueClassConstants(array $fileSegments): array
48 | {
49 | $classConstant = [];
50 | foreach ($fileSegments as $fileSegment) {
51 | foreach ($fileSegment->getClassConsts() as $currentClassConstant) {
52 | foreach ($currentClassConstant->consts as $currentConst) {
53 | if (isset($classConstant[(string)$currentConst->name])) {
54 | continue;
55 | }
56 | $classConstant[(string)$currentConst->name] = new ClassConst([$currentConst]);
57 | }
58 | }
59 | }
60 | return array_values($classConstant);
61 | }
62 |
63 | /**
64 | * @param Node[] $statements
65 | */
66 | protected function getNamespace(array $statements): ?Namespace_
67 | {
68 | $namespace = null;
69 | foreach ($statements as $statement) {
70 | if ($statement instanceof Namespace_) {
71 | $namespace = $statement;
72 | break;
73 | }
74 | }
75 | return $namespace;
76 | }
77 |
78 | protected function getClass(Namespace_ $namespace): ?Class_
79 | {
80 | /** @var ?Class_ $class */
81 | $class = null;
82 | foreach ($namespace->stmts as $node) {
83 | if ($node instanceof Class_) {
84 | $class = $node;
85 | break;
86 | }
87 | }
88 | return $class;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/ConstructorGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
39 | $class = $this->getClass($namespace);
40 |
41 | if ($class && $this->hasConstructor($fileSegments)) {
42 | [$params, $stmts] = $this->getParamsAndStmts($fileSegments);
43 | $class->stmts[] = new ClassMethod(
44 | '__construct',
45 | [
46 | 'flags' => Modifiers::PUBLIC,
47 | 'params' => $params,
48 | 'stmts' => $stmts,
49 | ]
50 | );
51 | }
52 |
53 | return $statements;
54 | }
55 |
56 | /**
57 | * @param FileSegments[] $fileSegments
58 | * @return array
59 | */
60 | protected function getParamsAndStmts(array $fileSegments): array
61 | {
62 | $params = [];
63 | $stmts = [];
64 | foreach ($fileSegments as $fileSegment) {
65 | $constructor = $fileSegment->getConstructor();
66 | if (!$constructor) {
67 | continue;
68 | }
69 |
70 | $params = $this->getConstructorParameter($params, $constructor->params);
71 | $stmts = $this->getConstructorStatements($stmts, $constructor->stmts, $fileSegment->isBaseClass());
72 | }
73 |
74 | return [$params, $stmts];
75 | }
76 |
77 | /**
78 | * @param Param[] $result
79 | * @param Param[] $params
80 | * @return Param[]
81 | */
82 | protected function getConstructorParameter(array $result, array $params): array
83 | {
84 | foreach ($params as $param) {
85 | if (isset($result[$param->var->name])) {
86 | continue;
87 | }
88 | $result[$param->var->name] = $param;
89 | }
90 |
91 | return $result;
92 | }
93 |
94 | /**
95 | * @param Stmt[] $result
96 | * @param Stmt[]|Expression[] $stmts
97 | * @param bool $isBaseClass
98 | * @return Stmt[]
99 | */
100 | protected function getConstructorStatements(array $result, array $stmts, bool $isBaseClass): array
101 | {
102 | if ($isBaseClass) {
103 | $result = [...$result, ...$stmts];
104 | } else {
105 | /** @var Expression $stmt */
106 | foreach ($stmts as $stmt) {
107 | $expr = $stmt->expr;
108 | if (
109 | !(
110 | $expr instanceof StaticCall
111 | && (string)$expr->class === 'parent'
112 | && (string)$expr->name === '__construct'
113 | )
114 | ) {
115 | $result[] = $stmt;
116 | }
117 | }
118 | }
119 |
120 | return $result;
121 | }
122 |
123 | /**
124 | * @param FileSegments[] $fileSegments
125 | */
126 | protected function hasConstructor(array $fileSegments): bool
127 | {
128 | $result = false;
129 |
130 | foreach ($fileSegments as $fileSegment) {
131 | if ($fileSegment->getConstructor()) {
132 | $result = true;
133 | break;
134 | }
135 | }
136 |
137 | return $result;
138 | }
139 |
140 | /**
141 | * @param Node[] $statements
142 | */
143 | protected function getNamespace(array $statements): ?Namespace_
144 | {
145 | $namespace = null;
146 | foreach ($statements as $statement) {
147 | if ($statement instanceof Namespace_) {
148 | $namespace = $statement;
149 | break;
150 | }
151 | }
152 | return $namespace;
153 | }
154 |
155 | protected function getClass(Namespace_ $namespace): ?Class_
156 | {
157 | /** @var ?Class_ $class */
158 | $class = null;
159 | foreach ($namespace->stmts as $node) {
160 | if ($node instanceof Class_) {
161 | $class = $node;
162 | break;
163 | }
164 | }
165 | return $class;
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/FileCommentGenerator.php:
--------------------------------------------------------------------------------
1 | createCommentText($fileSegments);
27 |
28 | $nop = new Nop();
29 | $nop->setDocComment(new Doc($commentText));
30 |
31 | $statements[] = $nop;
32 |
33 | return $statements;
34 | }
35 |
36 | /**
37 | * @param FileSegments[] $fileSegments
38 | */
39 | protected function createCommentText(array $fileSegments): string
40 | {
41 | $fileComment = [
42 | '/*',
43 | ' * This file is composed by "extender"',
44 | ' * Merged class with parts of files:',
45 | ];
46 |
47 | foreach ($fileSegments as $fileSegment) {
48 | $fileComment[] = ' * - ' . $fileSegment->getFilePath();
49 | }
50 |
51 | $fileComment[] = ' */';
52 |
53 | return implode(chr(10), $fileComment) . chr(10);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/GeneratorInterface.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
39 | $class = $this->getClass($namespace);
40 |
41 | if ($class && $this->hasInitializeObject($fileSegments)) {
42 | [$params, $stmts] = $this->getParamsAndStmts($fileSegments);
43 | $class->stmts[] = new ClassMethod(
44 | 'initializeObject',
45 | [
46 | 'flags' => Modifiers::PUBLIC,
47 | 'params' => $params,
48 | 'stmts' => $stmts,
49 | ]
50 | );
51 | }
52 |
53 | return $statements;
54 | }
55 |
56 | /**
57 | * @param FileSegments[] $fileSegments
58 | * @return array
59 | */
60 | protected function getParamsAndStmts(array $fileSegments): array
61 | {
62 | $params = [];
63 | $stmts = [];
64 | foreach ($fileSegments as $fileSegment) {
65 | $initializeObject = $fileSegment->getInitializeObject();
66 | if (!$initializeObject) {
67 | continue;
68 | }
69 |
70 | $params = $this->getInitializeObjectParameter($params, $initializeObject->getParams());
71 | $stmts = $this->getInitializeObjectStatements(
72 | $stmts,
73 | $initializeObject->getStmts(),
74 | $fileSegment->isBaseClass()
75 | );
76 | }
77 |
78 | return [$params, $stmts];
79 | }
80 |
81 | /**
82 | * @param Param[] $result
83 | * @param Param[] $params
84 | * @return Param[]
85 | */
86 | protected function getInitializeObjectParameter(array $result, array $params): array
87 | {
88 | foreach ($params as $param) {
89 | if (isset($result[$param->var->name])) {
90 | continue;
91 | }
92 | $result[$param->var->name] = $param;
93 | }
94 |
95 | return $result;
96 | }
97 |
98 | /**
99 | * @param Stmt[] $result
100 | * @param Stmt[]|Expression[] $stmts
101 | * @param bool $isBaseClass
102 | * @return Stmt[]
103 | */
104 | protected function getInitializeObjectStatements(array $result, array $stmts, bool $isBaseClass): array
105 | {
106 | if ($isBaseClass) {
107 | $result = [...$result, ...$stmts];
108 | } else {
109 | foreach ($stmts as $stmt) {
110 | /** @var Expression|StaticCall $stmt */
111 | $expr = $stmt->expr;
112 | if (
113 | !(
114 | $expr instanceof StaticCall
115 | && (string)$expr->class === 'parent'
116 | && (string)$expr->name === 'initializeObject'
117 | )
118 | ) {
119 | $result[] = $stmt;
120 | }
121 | }
122 | }
123 |
124 | return $result;
125 | }
126 |
127 | /**
128 | * @param FileSegments[] $fileSegments
129 | */
130 | protected function hasInitializeObject(array $fileSegments): bool
131 | {
132 | $result = false;
133 | foreach ($fileSegments as $fileSegment) {
134 | if ($fileSegment->getInitializeObject()) {
135 | $result = true;
136 | break;
137 | }
138 | }
139 | return $result;
140 | }
141 |
142 | /**
143 | * @param Node[] $statements
144 | */
145 | protected function getNamespace(array $statements): ?Namespace_
146 | {
147 | $namespace = null;
148 | foreach ($statements as $statement) {
149 | if ($statement instanceof Namespace_) {
150 | $namespace = $statement;
151 | break;
152 | }
153 | }
154 | return $namespace;
155 | }
156 |
157 | protected function getClass(Namespace_ $namespace): ?Class_
158 | {
159 | /** @var ?Class_ $class */
160 | $class = null;
161 | foreach ($namespace->stmts as $node) {
162 | if ($node instanceof Class_) {
163 | $class = $node;
164 | break;
165 | }
166 | }
167 | return $class;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/NamespaceGenerator.php:
--------------------------------------------------------------------------------
1 | isBaseClass()) {
33 | continue;
34 | }
35 | $statements[] = new Namespace_($fileSegment->getNamespace());
36 | }
37 |
38 | return $statements;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/PropertyGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
35 | $class = $this->getClass($namespace);
36 |
37 | if ($class) {
38 | $class->stmts = [...$class->stmts, ...$this->getUniqueProperties($fileSegments)];
39 | }
40 |
41 | return $statements;
42 | }
43 |
44 | /**
45 | * @param FileSegments[] $fileSegments
46 | * @return Property[]
47 | */
48 | protected function getUniqueProperties(array $fileSegments): array
49 | {
50 | $properties = [];
51 | foreach ($fileSegments as $fileSegment) {
52 | foreach ($fileSegment->getProperties() as $property) {
53 | if (count($property->props) == 1) {
54 | $propertyProperty = $property->props[0];
55 | $properties[(string)$propertyProperty->name] = $property;
56 | } else {
57 | foreach ($property->props as $propertyProperty) {
58 | $properties[(string)$propertyProperty->name] = new Property(
59 | Modifiers::PROTECTED,
60 | [$propertyProperty]
61 | );
62 | }
63 | }
64 | }
65 | }
66 | return array_values($properties);
67 | }
68 |
69 | /**
70 | * @param Node[] $statements
71 | */
72 | protected function getNamespace(array $statements): ?Namespace_
73 | {
74 | $namespace = null;
75 | foreach ($statements as $statement) {
76 | if ($statement instanceof Namespace_) {
77 | $namespace = $statement;
78 | break;
79 | }
80 | }
81 | return $namespace;
82 | }
83 |
84 | protected function getClass(Namespace_ $namespace): ?Class_
85 | {
86 | /** @var ?Class_ $class */
87 | $class = null;
88 | foreach ($namespace->stmts as $node) {
89 | if ($node instanceof Class_) {
90 | $class = $node;
91 | break;
92 | }
93 | }
94 | return $class;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/TraitGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
34 | $class = $this->getClass($namespace);
35 |
36 | if ($class) {
37 | $class->stmts = [...$class->stmts, ...$this->getUniqueTraits($fileSegments)];
38 | }
39 |
40 | return $statements;
41 | }
42 |
43 | /**
44 | * @param FileSegments[] $fileSegments
45 | * @return TraitUse[]
46 | */
47 | protected function getUniqueTraits(array $fileSegments): array
48 | {
49 | $traits = [];
50 | foreach ($fileSegments as $fileSegment) {
51 | foreach ($fileSegment->getTraits() as $currentTraits) {
52 | foreach ($currentTraits->traits as $currentTrait) {
53 | if (isset($traits[(string)$currentTrait])) {
54 | continue;
55 | }
56 | $traits[(string)$currentTrait] = new TraitUse([$currentTrait]);
57 | }
58 | }
59 | }
60 | return array_values($traits);
61 | }
62 |
63 | /**
64 | * @param Node[] $statements
65 | */
66 | protected function getNamespace(array $statements): ?Namespace_
67 | {
68 | $namespace = null;
69 | foreach ($statements as $statement) {
70 | if ($statement instanceof Namespace_) {
71 | $namespace = $statement;
72 | break;
73 | }
74 | }
75 | return $namespace;
76 | }
77 |
78 | protected function getClass(Namespace_ $namespace): ?Class_
79 | {
80 | /** @var ?Class_ $class */
81 | $class = null;
82 | foreach ($namespace->stmts as $node) {
83 | if ($node instanceof Class_) {
84 | $class = $node;
85 | break;
86 | }
87 | }
88 | return $class;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Classes/Composer/Generator/UseGenerator.php:
--------------------------------------------------------------------------------
1 | getNamespace($statements);
34 |
35 | $uses = $this->getUniqueUses($fileSegments);
36 | foreach ($uses as $use) {
37 | $namespace->stmts[] = new Use_([$use]);
38 | }
39 |
40 | return $statements;
41 | }
42 |
43 | /**
44 | * @param FileSegments[] $fileSegments
45 | * @return UseItem[]
46 | */
47 | protected function getUniqueUses(array $fileSegments): array
48 | {
49 | $uses = [];
50 | foreach ($fileSegments as $fileSegment) {
51 | foreach ($fileSegment->getUses() as $use) {
52 | $name = $use->name . $use->getAlias();
53 | if (isset($uses[$name])) {
54 | continue;
55 | }
56 | $uses[$name] = $use;
57 | }
58 | }
59 | return array_values($uses);
60 | }
61 |
62 | /**
63 | * @param Node[] $statements
64 | */
65 | protected function getNamespace(array $statements): ?Namespace_
66 | {
67 | $namespace = null;
68 | foreach ($statements as $statement) {
69 | if ($statement instanceof Namespace_) {
70 | $namespace = $statement;
71 | break;
72 | }
73 | }
74 | return $namespace;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Classes/Configuration/ClassRegister.php:
--------------------------------------------------------------------------------
1 | $extendedClasses
22 | */
23 | public function __construct(protected array $extendedClasses = []) {}
24 |
25 | public function hasBaseClassName(string $className): bool
26 | {
27 | return isset($this->extendedClasses[$className]);
28 | }
29 |
30 | /**
31 | * @return string[]
32 | */
33 | public function getExtendingClasses(string $className): array
34 | {
35 | return is_array($this->extendedClasses[$className] ?? false)
36 | ? $this->extendedClasses[$className]
37 | : [];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Classes/DependencyInjection/RegisterExtenderPass.php:
--------------------------------------------------------------------------------
1 | addRegisterAutoloadEventToEventDispatcher($container);
29 | $this->addExtendedClassesToRegisterDefinition($container);
30 | }
31 |
32 | protected function addRegisterAutoloadEventToEventDispatcher(ContainerBuilder $container): void
33 | {
34 | $registerAutoloaderEvent = $container->findDefinition(RegisterAutoloaderEvent::class);
35 | $eventDispatcher = $container->findDefinition(EventDispatcherInterface::class);
36 | $eventDispatcher->addMethodCall('dispatch', [$registerAutoloaderEvent]);
37 | }
38 |
39 | protected function addExtendedClassesToRegisterDefinition(ContainerBuilder $container): void
40 | {
41 | $extendedClasses = [];
42 | foreach ($container->findTaggedServiceIds('extender.extends', true) as $extendingClass => $tags) {
43 | foreach ($tags as $tag) {
44 | $extendedClass = $tag['class'] ?? '';
45 | if ($extendedClass === '') {
46 | continue;
47 | }
48 |
49 | if (!isset($extendedClasses[$extendedClass])) {
50 | $extendedClasses[$extendedClass] = [];
51 | }
52 | $extendedClasses[$extendedClass][] = $extendingClass;
53 | }
54 | }
55 |
56 | $classRegisterDefinition = $container->getDefinition(ClassRegister::class);
57 | $classRegisterDefinition->setArguments([$extendedClasses]);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Classes/Event/RegisterAutoloaderEvent.php:
--------------------------------------------------------------------------------
1 | get(ClassLoader::class), 'loadClass'];
29 | if ($this->autoloaderAlreadyRegistered($autoloader)) {
30 | $this->unregisterAutoloader($autoloader);
31 | }
32 | spl_autoload_register($autoloader, true, true);
33 | } catch (ContainerExceptionInterface) {
34 | }
35 | }
36 |
37 | /**
38 | * @param array $autoloader
39 | */
40 | protected function autoloaderAlreadyRegistered(array $autoloader): bool
41 | {
42 | $result = false;
43 |
44 | $autoloaderClass = get_class($autoloader[0]);
45 | $currentAutoLoaders = spl_autoload_functions();
46 | foreach ($currentAutoLoaders as $currentAutoLoader) {
47 | if (
48 | is_array($currentAutoLoader)
49 | && (
50 | (is_object($currentAutoLoader[0]) && get_class($currentAutoLoader[0]) === $autoloaderClass)
51 | || (is_string($currentAutoLoader[0]) && $currentAutoLoader[0] === $autoloaderClass)
52 | )
53 | ) {
54 | $result = true;
55 | break;
56 | }
57 | }
58 |
59 | return $result;
60 | }
61 |
62 | /**
63 | * @param array $autoloader
64 | */
65 | protected function unregisterAutoloader(array $autoloader): void
66 | {
67 | spl_autoload_unregister($autoloader);
68 | }
69 |
70 | public function isPropagationStopped(): bool
71 | {
72 | return true;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Classes/Exception/BaseFileNotFoundException.php:
--------------------------------------------------------------------------------
1 | $parameters
16 | */
17 | public function clearCachePostProc(array $parameters): void
18 | {
19 | if (Environment::getContext()->isDevelopment() && ($parameters['cacheCmd'] ?? '') === 'all') {
20 | $this->classCache->flush();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Classes/Loader/ClassLoader.php:
--------------------------------------------------------------------------------
1 | isValidClassName($className)) {
41 | $cacheEntryIdentifier = str_replace('\\', '_', $className);
42 |
43 | if (!$this->classCache->has($cacheEntryIdentifier)) {
44 | $this->classCacheManager->build($cacheEntryIdentifier, $className);
45 | }
46 |
47 | $this->classCache->requireOnce($cacheEntryIdentifier);
48 | $return = true;
49 | }
50 |
51 | return $return;
52 | }
53 |
54 | protected function isValidClassName(string $className): bool
55 | {
56 | return $this->classRegister->hasBaseClassName($className);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Classes/Parser/ClassParser.php:
--------------------------------------------------------------------------------
1 | setFilePath($filePath);
45 | $fileSegments->setCode(file_get_contents($filePath));
46 |
47 | try {
48 | // @extensionScannerIgnoreLine
49 | // @phpstan-ignore method.notFound
50 | $parser = $this->parserFactory->createForVersion(PhpVersion::fromComponents(8, 2));
51 | $fileSegments->setStatements($parser->parse($fileSegments->getCode()));
52 |
53 | foreach ($this->visitors as $visitor) {
54 | $this->traverseStatements($fileSegments, $visitor);
55 | }
56 | } catch (\Exception) {
57 | }
58 |
59 | return $fileSegments;
60 | }
61 |
62 | protected function traverseStatements(FileSegments $fileSegment, string $visitorClassName): void
63 | {
64 | $visitor = new $visitorClassName($fileSegment);
65 |
66 | $traverser = new NodeTraverser();
67 | $traverser->addVisitor($visitor);
68 | $traverser->traverse($fileSegment->getStatements());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Classes/Parser/FileSegments.php:
--------------------------------------------------------------------------------
1 | filePath;
76 | }
77 |
78 | public function setFilePath(string $filePath): void
79 | {
80 | $this->filePath = $filePath;
81 | }
82 |
83 | public function isBaseClass(): bool
84 | {
85 | return $this->baseClass;
86 | }
87 |
88 | public function setBaseClass(bool $baseClass): void
89 | {
90 | $this->baseClass = $baseClass;
91 | }
92 |
93 | public function getCode(): string
94 | {
95 | return $this->code;
96 | }
97 |
98 | public function setCode(string $code): void
99 | {
100 | $this->code = $code;
101 | }
102 |
103 | /**
104 | * @return Node[]
105 | */
106 | public function getStatements(): array
107 | {
108 | return $this->statements;
109 | }
110 |
111 | /**
112 | * @param Node[] $statements
113 | */
114 | public function setStatements(array $statements): void
115 | {
116 | $this->statements = $statements;
117 | }
118 |
119 | public function getNamespace(): ?Name
120 | {
121 | return $this->namespace;
122 | }
123 |
124 | public function setNamespace(?Name $namespace): void
125 | {
126 | $this->namespace = $namespace;
127 | }
128 |
129 | /**
130 | * @return UseItem[]
131 | */
132 | public function getUses(): array
133 | {
134 | return $this->uses;
135 | }
136 |
137 | /**
138 | * @param UseItem[] $uses
139 | */
140 | public function setUses(array $uses): void
141 | {
142 | $this->uses = $uses;
143 | }
144 |
145 | public function addUse(UseItem $use): void
146 | {
147 | $this->uses[] = $use;
148 | }
149 |
150 | public function getClass(): ?Class_
151 | {
152 | return $this->class;
153 | }
154 |
155 | public function setClass(?Class_ $class): void
156 | {
157 | $this->class = $class;
158 | }
159 |
160 | /**
161 | * @return TraitUse[]
162 | */
163 | public function getTraits(): array
164 | {
165 | return $this->traits;
166 | }
167 |
168 | /**
169 | * @param TraitUse[] $traits
170 | */
171 | public function setTraits(array $traits): void
172 | {
173 | $this->traits = $traits;
174 | }
175 |
176 | public function addTrait(TraitUse $traitUse): void
177 | {
178 | $this->traits[] = $traitUse;
179 | }
180 |
181 | /**
182 | * @return ClassConst[]
183 | */
184 | public function getClassConsts(): array
185 | {
186 | return $this->classConsts;
187 | }
188 |
189 | /**
190 | * @param ClassConst[] $classConst
191 | */
192 | public function setClassConsts(array $classConst): void
193 | {
194 | $this->classConsts = $classConst;
195 | }
196 |
197 | public function addClassConst(ClassConst $classConst): void
198 | {
199 | $this->classConsts[] = $classConst;
200 | }
201 |
202 | /**
203 | * @return Property[]
204 | */
205 | public function getProperties(): array
206 | {
207 | return $this->properties;
208 | }
209 |
210 | /**
211 | * @param Property[] $properties
212 | */
213 | public function setProperties(array $properties): void
214 | {
215 | $this->properties = $properties;
216 | }
217 |
218 | public function addProperty(Property $property): void
219 | {
220 | $this->properties[] = $property;
221 | }
222 |
223 | public function getConstructor(): ?ClassMethod
224 | {
225 | return $this->constructor;
226 | }
227 |
228 | public function setConstructor(?ClassMethod $constructor): void
229 | {
230 | $this->constructor = $constructor;
231 | }
232 |
233 | public function getInitializeObject(): ?ClassMethod
234 | {
235 | return $this->initializeObject;
236 | }
237 |
238 | public function setInitializeObject(?ClassMethod $initializeObject): void
239 | {
240 | $this->initializeObject = $initializeObject;
241 | }
242 |
243 | /**
244 | * @return ClassMethod[]
245 | */
246 | public function getMethods(): array
247 | {
248 | return $this->methods;
249 | }
250 |
251 | /**
252 | * @param ClassMethod[] $methods
253 | */
254 | public function setMethods(array $methods): void
255 | {
256 | $this->methods = $methods;
257 | }
258 |
259 | public function addMethod(ClassMethod $classMethod): void
260 | {
261 | $this->methods[] = $classMethod;
262 | }
263 |
264 | /**
265 | * @return array
266 | */
267 | public function jsonSerialize(): array
268 | {
269 | return [
270 | 'filePath' => $this->filePath,
271 | 'baseClass' => $this->baseClass,
272 | 'code' => $this->code,
273 | 'statements' => $this->statements,
274 | 'namespace' => $this->namespace,
275 | 'uses' => $this->uses,
276 | 'class' => $this->class,
277 | 'traits' => $this->traits,
278 | 'properties' => $this->properties,
279 | 'constructor' => $this->constructor,
280 | 'methods' => $this->methods,
281 | ];
282 | }
283 | }
284 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/AbstractVisitor.php:
--------------------------------------------------------------------------------
1 | name, $this->disallowedMethodNames)
39 | ) {
40 | $this->fileSegment->addMethod($node);
41 | }
42 | return $node;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/ClassVisitor.php:
--------------------------------------------------------------------------------
1 | stmts = [];
31 |
32 | $this->fileSegment->setClass($class);
33 | }
34 | return $node;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/ConstantVisitor.php:
--------------------------------------------------------------------------------
1 | fileSegment->addClassConst($node);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/ConstructorVisitor.php:
--------------------------------------------------------------------------------
1 | name === '__construct') {
29 | $this->fileSegment->setConstructor($node);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/InitializeObjectVisitor.php:
--------------------------------------------------------------------------------
1 | name === 'initializeObject') {
29 | $this->fileSegment->setInitializeObject($node);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/NamespaceVisitor.php:
--------------------------------------------------------------------------------
1 | fileSegment->setNamespace($node->name);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/PropertyVisitor.php:
--------------------------------------------------------------------------------
1 | fileSegment->addProperty($node);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/TraitVisitor.php:
--------------------------------------------------------------------------------
1 | fileSegment->addTrait($node);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Classes/Parser/Visitor/UseVisitor.php:
--------------------------------------------------------------------------------
1 | fileSegment->addUse($node);
30 | }
31 | return $node;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Configuration/Services.php:
--------------------------------------------------------------------------------
1 | addCompilerPass(new RegisterExtenderPass());
24 | };
25 |
--------------------------------------------------------------------------------
/Configuration/Services.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | _defaults:
3 | autowire: false
4 | autoconfigure: false
5 | public: false
6 |
7 | Evoweb\Extender\:
8 | resource: '../Classes/'
9 |
10 | cache.extender:
11 | class: TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
12 | # We can not use CacheManager, as it can not be
13 | # injected/instantiated prior (or during) ext_localconf.php
14 | # loading therefore we use an own factory instead.
15 | factory: ['@Evoweb\Extender\Cache\CacheFactory', 'createCache']
16 | arguments: ['extender']
17 |
18 | Evoweb\Extender\Cache\ClassCacheManager:
19 | arguments:
20 | $classCache: '@cache.extender'
21 | $classLoader: '@Composer\Autoload\ClassLoader'
22 | $classParser: '@Evoweb\Extender\Parser\ClassParser'
23 | $classComposer: '@Evoweb\Extender\Composer\ClassComposer'
24 | $classRegister: '@Evoweb\Extender\Configuration\ClassRegister'
25 | public: true
26 |
27 | Evoweb\Extender\Command\ClearCommand:
28 | tags:
29 | - name: 'console.command'
30 | command: 'extender:clearClassCache'
31 | description: 'CLI command for the "extender" extension - clear cache'
32 | schedulable: false
33 | arguments:
34 | $cacheFactory: '@Evoweb\Extender\Cache\CacheFactory'
35 |
36 | Evoweb\Extender\Composer\ClassComposer:
37 | public: true
38 |
39 | Evoweb\Extender\Configuration\ClassRegister:
40 | public: true
41 |
42 | Evoweb\Extender\Event\RegisterAutoloaderEvent:
43 | arguments:
44 | $container: '@service_container'
45 | shared: false
46 |
47 | Evoweb\Extender\Hooks\ClearCacheHook:
48 | public: true
49 | arguments:
50 | $classCache: '@cache.extender'
51 |
52 | Evoweb\Extender\Loader\ClassLoader:
53 | arguments:
54 | $classCache: '@cache.extender'
55 | $classCacheManager: '@Evoweb\Extender\Cache\ClassCacheManager'
56 | $classRegister: '@Evoweb\Extender\Configuration\ClassRegister'
57 | public: true
58 |
59 | Evoweb\Extender\Parser\ClassParser:
60 | arguments:
61 | $parserFactory: '@PhpParser\ParserFactory'
62 | public: true
63 |
64 | PhpParser\ParserFactory:
65 | public: true
66 |
--------------------------------------------------------------------------------
/Documentation/Breaking/Index.rst:
--------------------------------------------------------------------------------
1 | .. include:: /Includes.rst.txt
2 | .. index:: Breaking changes
3 | .. _breaking-changes:
4 |
5 | ===============
6 | Breaking change
7 | ===============
8 |
9 | Change of command name
10 | ======================
11 |
12 | The command was renamed from "extender:rebuild" to "extender:clearClassCache"
13 | to better reflect what the command is doing.
14 |
15 | Change of extending configuration in 10.0.0
16 | ===========================================
17 |
18 | Description
19 | -----------
20 |
21 | Since version 10.0.0 the registration happens in services.yaml
22 |
23 | Impact
24 | ------
25 |
26 | All class extending in ext_localconf.php needs to be replaced and converted
27 |
28 | Migration
29 | ---------
30 |
31 | Migrate configuration from array to yaml.
32 |
33 | .. code-block:: php
34 | :caption: before ext_localconf.php
35 |
36 | $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['base_extension']['extender'][
37 | \Fixture\BaseExtension\Domain\Model\Blob::class
38 | ]['extending_extension'] = 'EXT:extending_extension/Classes/Domain/Model/BlobExtend.php';
39 |
40 | .. code-block:: yaml
41 | :caption: after Services.yaml
42 |
43 | Fixture\ExtendingExtension\Domain\Model\BlobExtend:
44 | tags:
45 | -
46 | name: 'extender.extends'
47 | class: Fixture\BaseExtension\Domain\Model\Blob
48 |
49 | Change of extending configuration in 7.0.0
50 | ==========================================
51 |
52 | Description
53 | -----------
54 |
55 | Since version 7.0.0 all usage of EXTCONF is replaced with EXTENSIONS.
56 |
57 | Impact
58 | ------
59 |
60 | All class extending still using EXTCONF to not work anymore. So the code still
61 | fills the array but this array is not used anymore.
62 |
63 | Affected Installations
64 | ----------------------
65 |
66 | All extensions that use EXTCONF in registration of class extending like.
67 |
68 | .. code-block:: php
69 | :caption: before ext_localconf.php
70 |
71 | $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['store_finder']['extender'][
72 | \Evoweb\StoreFinder\Domain\Model\Location::class
73 | ]['sitepackage'] = 'EXT:sitepackage/Classes/Domain/Model/Location.php';
74 |
75 | Migration
76 | ---------
77 |
78 | Replace the usage of EXTCONF with EXTENSIONS to have the class extended again.
79 |
--------------------------------------------------------------------------------
/Documentation/Configuration/Index.rst:
--------------------------------------------------------------------------------
1 | .. include:: /Includes.rst.txt
2 | .. index:: Configuration
3 | .. _configuration:
4 |
5 | =============
6 | Configuration
7 | =============
8 |
9 | Configure the extend of a class
10 | ===============================
11 |
12 | Since version 10.0.0 the registration of class extends needs to be configured in
13 | services.yaml like in this example.
14 |
15 | .. code-block:: yaml
16 | :caption: Services.yaml
17 |
18 | services:
19 | Fixture\ExtendingExtension\Domain\Model\BlobExtend:
20 | tags:
21 | -
22 | name: 'extender.extends'
23 | class: Fixture\BaseExtension\Domain\Model\Blob
24 |
25 | - Fixture\BaseExtension\Domain\Model\Blob is the class that should be extended
26 |
27 | - Fixture\ExtendingExtension\Domain\Model\BlobExtend is the class that extends
28 |
--------------------------------------------------------------------------------
/Documentation/Includes.rst.txt:
--------------------------------------------------------------------------------
1 | .. You can put central messages to display on all pages here
2 |
--------------------------------------------------------------------------------
/Documentation/Index.rst:
--------------------------------------------------------------------------------
1 | .. include:: /Includes.rst.txt
2 |
3 | .. _start:
4 |
5 | =============================================
6 | Extender: Extbase class extending on steroids
7 | =============================================
8 |
9 | :Extension key:
10 | extender
11 |
12 | :Package name:
13 | evoweb/extender
14 |
15 | :Version:
16 | |release|
17 |
18 | :Language:
19 | en
20 |
21 | :Author:
22 | Sebastian Fischer
23 |
24 | :License:
25 | This document is published under the
26 | `Open Publication `__.
27 | license.
28 |
29 | :Rendered:
30 | |today|
31 |
32 | :Copyright:
33 | 2014-2023
34 |
35 | ----
36 |
37 | Makes extending classes fully transparent
38 |
39 | The content of this document is related to TYPO3,
40 | a GNU/GPL CMS/Framework available from http://typo3.org
41 |
42 | ----
43 |
44 | **Table of Contents**
45 |
46 | .. toctree::
47 | :maxdepth: 2
48 | :titlesonly:
49 |
50 | Introduction/Index
51 | Installation/Index
52 | Configuration/Index
53 | Breaking/Index
54 | Testing/Index
55 |
56 | .. Meta Menu
57 |
58 | .. toctree::
59 | :hidden:
60 |
61 | Sitemap
62 |
--------------------------------------------------------------------------------
/Documentation/Installation/Index.rst:
--------------------------------------------------------------------------------
1 | .. include:: /Includes.rst.txt
2 | .. index:: Installation
3 | .. _installation:
4 |
5 | ============
6 | Installation
7 | ============
8 |
9 | As EXT:extender is based on composer and its mechanisms the installation is only
10 | possible with composer methods.
11 |
12 | **WARNING** In previous versions it was possible to use extender when installed
13 | via the Extension Manager. This is not possible anymore.
14 |
15 | Require via command
16 | ===================
17 |
18 | You can add extender with composer require.
19 |
20 | .. code-block:: bash
21 | composer require evoweb/extender
22 |
23 | Modify composer.json
24 | ====================
25 |
26 | Additionally evoweb/extender can be added to the require in your composer.json,
27 | like in the following example and run 'composer install'.
28 |
29 | .. code-block:: json
30 | :caption: composer.json
31 |
32 | {
33 | "require": {
34 | ...
35 | "evoweb/extender": "*"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Documentation/Introduction/Images/base.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evoWeb/extender/279bf9bbc00a2c5ea300a14a8d5445cf006e269d/Documentation/Introduction/Images/base.png
--------------------------------------------------------------------------------
/Documentation/Introduction/Images/extend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evoWeb/extender/279bf9bbc00a2c5ea300a14a8d5445cf006e269d/Documentation/Introduction/Images/extend.png
--------------------------------------------------------------------------------
/Documentation/Introduction/Images/merged.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evoWeb/extender/279bf9bbc00a2c5ea300a14a8d5445cf006e269d/Documentation/Introduction/Images/merged.png
--------------------------------------------------------------------------------
/Documentation/Introduction/Index.rst:
--------------------------------------------------------------------------------
1 | .. include:: /Includes.rst.txt
2 | .. index:: Introduction
3 | .. _introduction:
4 |
5 | ============
6 | Introduction
7 | ============
8 |
9 | This Documentation was written for version 7.0.x of the extension.
10 |
11 | Extending classes
12 | =================
13 |
14 | The sole purpose of this extension is to fill a gap that extbase leaves open.
15 |
16 | As it is not possible to extend classes in extbase directly its sometimes not
17 | possible to add properties and methods to a class. This is valid for all cases
18 | where the class is given as an type hint to an action.
19 |
20 | Arguments of an action are mapped from the request to the concrete class by
21 | the argument mapper. The argument mapper does not take TypoScript object
22 | className mapping into account as the property mapper. Due to this its not
23 | possible to just extend the class and have the extends available.
24 |
25 | To cope with this and be able to add custom properties to a class the extender
26 | registers a custom spl class loader. This kicks in for every configured class
27 | and required a compiled class from the class cache.
28 |
29 | The class cache gets generated on every hit where an configured compilation is
30 | not available and after every clear system cache. In both cases the class cache
31 | manager rebuilds the complete class cache.
32 |
33 | As the class cache is registered to the system cache group it gets cleared on
34 | every clear system cache or clear all caches. After that a hook gets called that
35 | rebuilds the class cache. Unless a huge amount of extends are configured there
36 | should be a prefilled class cache on every request.
37 |
38 | Deep dive into an example
39 | =========================
40 |
41 | The cache manager parses the base and extend files to gather the colored parts
42 | of the following images, generates a combined file as shown in the merged result
43 | and adds it to the cache.
44 |
45 | .. rst-class:: bignums
46 |
47 | - Base class file
48 |
49 | .. figure:: Images/base.png
50 |
51 | - Extend class file
52 |
53 | .. figure:: Images/extend.png
54 |
55 | - Merged result file
56 |
57 | .. figure:: Images/merged.png
58 |
59 | Explanation
60 | ===========
61 |
62 | .. rst-class:: bignums-attention
63 |
64 | - Namespace
65 |
66 | The Namespace is taken from the base file. The namespace of the extended
67 | file is ignored.
68 |
69 | - Uses
70 |
71 | All uses from base and extending file are taken uniquely. If an use appears
72 | with diverting as alias it is present twice in the merged file.
73 |
74 | .. code-block::
75 | :caption: example of uses appearing twice
76 |
77 | use Psr\Log\LoggerAwareTrait;
78 | use Psr\Log\LoggerAwareTrait as T;
79 |
80 | - Class
81 |
82 | The class name and the extends part is taken from the base class.
83 |
84 | - Implements
85 |
86 | Implements are used uniquely from the base and extend file
87 |
88 | - Traits
89 |
90 | All traits from base and extend file are taken uniquely.
91 |
92 | - Properties
93 |
94 | All properties from base and extend file are taken without check if they
95 | are not colliding.
96 |
97 | - Construct
98 |
99 | The __construct of base and extend file are taken with merged contents and
100 | arguments. Where arguments from base take priority.
101 | All line of code in the method are taken. If the __construct of the extend
102 | file contains a parent::__construct call it gets removed.
103 |
104 | - Methods
105 |
106 | All methods beside __construct from base and extend file are taken without
107 | check if they are not colliding.
108 |
109 | - Comment
110 |
111 | The comment is based on the base and extending files and display which
112 | files path were taken into account.
113 |
114 | Example source
115 | ==============
116 |
117 | The base file content could be found in
118 | EXT:extender/Tests/Fixtures/Extensions/base_extension/Classes/Domain/Model/Blob.php
119 |
120 | The extend file content is derived from
121 | EXT:extender/Tests/Fixtures/Extensions/extending_extension/Classes/Domain/Model/BlobExtend.php
122 |
123 | Important
124 | =========
125 |
126 | Correct
127 | -----
128 |
129 | As in both files shown, it's important to use the FQCN to extend of, else the
130 | usage of the class gets written to the merged file and result in two classes
131 | with the same name in the cache file.
132 |
133 | .. code-block:: php
134 | :caption: Correct extending
135 |
136 | namespace Fixture\ExtendingClass\Domain\Model;
137 |
138 | class ExtendingModel extends \Fixture\BaseClass\Domain\Model\BaseModel
139 | {
140 | }
141 |
142 | .. code-block:: php
143 | :caption: Result with correct extending
144 |
145 | namespace Fixture\ExtendingClass\Domain\Model;
146 |
147 | class BaseModel
148 | {
149 | }
150 |
151 | Wrong
152 | -----
153 |
154 | While linting the file will not raise an error and the class is usable,
155 | it will definitely irritate editors like PHPStorm or Visual Code.
156 |
157 | .. code-block:: php
158 | :caption: Wrong extension
159 |
160 | namespace Fixture\ExtendingClass\Domain\Model;
161 |
162 | use Fixture\BaseClass\Domain\Model\BaseModel;
163 |
164 | class ExtendingModel extends BaseModel
165 | {
166 | }
167 |
168 | .. code-block:: php
169 | :caption: Result with wrong extension
170 |
171 | namespace Fixture\BaseClass\Domain\Model;
172 |
173 | use Fixture\BaseClass\Domain\Model\BaseModel;
174 |
175 | class BaseModel
176 | {
177 | }
178 |
--------------------------------------------------------------------------------
/Documentation/Sitemap.rst:
--------------------------------------------------------------------------------
1 | :template: sitemap.html
2 |
3 | .. include:: /Includes.rst.txt
4 |
5 | =======
6 | Sitemap
7 | =======
8 |
9 | .. The sitemap.html template will insert here the page tree automatically.
10 |
--------------------------------------------------------------------------------
/Documentation/Testing/Images/phpstorm_configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evoWeb/extender/279bf9bbc00a2c5ea300a14a8d5445cf006e269d/Documentation/Testing/Images/phpstorm_configuration.png
--------------------------------------------------------------------------------
/Documentation/Testing/Index.rst:
--------------------------------------------------------------------------------
1 | .. include:: /Includes.rst.txt
2 | .. index:: Testing
3 | .. _testing:
4 |
5 | =======
6 | Testing
7 | =======
8 |
9 | .. figure:: Images/phpstorm_configuration.png
10 | :alt: Setting up configuration for Functional Testing in PhpStorm
11 |
--------------------------------------------------------------------------------
/Documentation/guides.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
17 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 |
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, see .
308 |
309 | Also add information on how to contact you by electronic and paper mail.
310 |
311 | If the program is interactive, make it output a short notice like this
312 | when it starts in an interactive mode:
313 |
314 | Gnomovision version 69, Copyright (C) year name of author
315 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
316 | This is free software, and you are welcome to redistribute it
317 | under certain conditions; type `show c' for details.
318 |
319 | The hypothetical commands `show w' and `show c' should show the appropriate
320 | parts of the General Public License. Of course, the commands you use may
321 | be called something other than `show w' and `show c'; they could even be
322 | mouse-clicks or menu items--whatever suits your program.
323 |
324 | You should also get your employer (if you work as a programmer) or your
325 | school, if any, to sign a "copyright disclaimer" for the program, if
326 | necessary. Here is a sample; alter the names:
327 |
328 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
329 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
330 |
331 | , 1 April 1989
332 | Moe Ghoul, President of Vice
333 |
334 | This General Public License does not permit incorporating your program into
335 | proprietary programs. If your program is a subroutine library, you may
336 | consider it more useful to permit linking proprietary applications with the
337 | library. If this is what you want to do, use the GNU Lesser General
338 | Public License instead of this License.
339 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TYPO3 Extending extbase domain models
2 |
3 | 
4 | [](https://packagist.org/packages/evoweb/extender)
5 | [](https://packagist.org/packages/evoweb/extender)
6 | [](https://packagist.org/packages/evoweb/extender)
7 |
8 | ## Installation
9 |
10 | ### via Composer
11 |
12 | The recommended way to install EXT:extender is by using [Composer](https://getcomposer.org):
13 |
14 | composer require evoweb/extender
15 |
16 | ### quick introduction
17 |
18 | Add the extending classname to your packages Services.yaml and add a tag to it.
19 | The tag must contain the name 'extender.extends' and the class it is extending.
20 |
21 | Services.yaml
22 | ```yaml
23 | services:
24 |
25 | Fixture\ExtendingExtension\Domain\Model\BlobExtend:
26 | tags:
27 | -
28 | name: 'extender.extends'
29 | class: Fixture\BaseExtension\Domain\Model\Blob
30 | ```
31 |
--------------------------------------------------------------------------------
/Resources/Public/Icons/Extension.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "evoweb/extender",
3 | "type": "typo3-cms-extension",
4 | "description": "Extending extbase domain models like a pro with extender",
5 | "homepage": "https://www.evoweb.de",
6 | "license": [
7 | "GPL-2.0-or-later"
8 | ],
9 | "keywords": [
10 | "TYPO3",
11 | "extbase",
12 | "extending",
13 | "domain models"
14 | ],
15 | "support" : {
16 | "source" : "https://github.com/evoWeb/extender",
17 | "issues" : "https://github.com/evoWeb/extender/issues",
18 | "docs": "https://docs.typo3.org/p/evoweb/extender/main/en-us/"
19 | },
20 | "config": {
21 | "vendor-dir": "Build/vendor",
22 | "bin-dir": "bin",
23 | "allow-plugins": {
24 | "typo3/class-alias-loader": true,
25 | "typo3/cms-composer-installers": true
26 | }
27 | },
28 | "require": {
29 | "typo3/cms-core": "^13.0 || 13.3.x-dev || dev-main",
30 | "nikic/php-parser": "^5.3",
31 | "psr/container": "^2.0",
32 | "psr/event-dispatcher": "^1.0",
33 | "symfony/console": "^7.0",
34 | "symfony/dependency-injection": "^7.0"
35 | },
36 | "require-dev": {
37 | "friendsofphp/php-cs-fixer": "^3.64.0",
38 | "friendsoftypo3/phpstan-typo3": "^0.9.0",
39 | "phpstan/phpdoc-parser": "^1.30.0",
40 | "phpstan/phpstan": "^1.12.5",
41 | "phpunit/phpunit": "^11.0.3",
42 | "typo3/testing-framework": "dev-main",
43 | "evowebtests/base-extension": "*",
44 | "evowebtests/extending-extension": "*",
45 | "typo3/cms-extensionmanager": "^13.0 || 13.0.x-dev || dev-main"
46 | },
47 | "minimum-stability": "dev",
48 | "prefer-stable": true,
49 | "extra": {
50 | "typo3/cms": {
51 | "extension-key": "extender",
52 | "app-dir": "Build",
53 | "web-dir": "Build/Web"
54 | }
55 | },
56 | "scripts": {
57 | "prepare-release": [
58 | "rm -rf .github",
59 | "rm -rf Build",
60 | "rm -rf Tests",
61 | "rm .gitattributes",
62 | "rm .gitignore",
63 | "sed -i \"s/version' => '.*'/version' => '$(echo ${GITHUB_REF} | cut -d / -f 3)'/\" ext_emconf.php\n"
64 | ],
65 | "post-install-cmd": [
66 | "ln -sf vendor/typo3/testing-framework/Resources/Core/Build/ Build/phpunit;"
67 | ],
68 | "post-update-cmd": [
69 | "@post-install-cmd"
70 | ],
71 | "post-autoload-dump": [
72 | "TYPO3\\TestingFramework\\Composer\\ExtensionTestEnvironment::prepare"
73 | ]
74 | },
75 | "autoload": {
76 | "psr-4": {
77 | "Evoweb\\Extender\\": "Classes/"
78 | }
79 | },
80 | "autoload-dev": {
81 | "psr-4": {
82 | "Evoweb\\Extender\\Tests\\": "Tests/"
83 | }
84 | },
85 | "repositories": {
86 | "files": {
87 | "type": "path",
88 | "url": "./Tests/Fixtures/Extensions/*"
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ext_emconf.php:
--------------------------------------------------------------------------------
1 | 'Extbase Domain Model Extender',
5 | 'description' => 'A services that enables adding properties and functions
6 | to classes by implementing the proxy pattern',
7 | 'category' => 'misc',
8 | 'author' => 'Sebastian Fischer',
9 | 'author_email' => 'extender@evoweb.de',
10 | 'author_company' => 'evoWeb',
11 | 'state' => 'stable',
12 | 'version' => '11.0.2',
13 | 'constraints' => [
14 | 'depends' => [
15 | 'typo3' => '13.0.0-13.4.99',
16 | ],
17 | ],
18 | ];
19 |
--------------------------------------------------------------------------------
/ext_localconf.php:
--------------------------------------------------------------------------------
1 |