Skip to content

Commit 60ace84

Browse files
committed
Initial commit
0 parents  commit 60ace84

19 files changed

Lines changed: 691 additions & 0 deletions

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Introduction
2+
Adds and removes traits & interfaces from classes in one command.
3+
4+
# Installation
5+
Install package.
6+
```bash
7+
composer require e2d2-dev/modify-classes
8+
```
9+
10+
# Usage
11+
It automatically detects if it is an interface or a trait. You can add or remove one or many in one step.
12+
Don't forget to use save.
13+
14+
## Add
15+
```php
16+
use Betta\ModifyClasses\Modifier;
17+
18+
// add one
19+
Modifier::make(Your::class)->add(YourInterface::class)->save()
20+
21+
// add many
22+
Modifier::make(Your::class)->addMany([
23+
YourInterface::class,
24+
YourSecondIntfaceOrTrait::class,
25+
])->save()
26+
```
27+
## Remove
28+
```php
29+
use Betta\ModifyClasses\Modifier;
30+
31+
// add one
32+
Modifier::make(Your::class)->remove(YourInterface::class)->save()
33+
34+
// add many
35+
Modifier::make(Your::class)->removeMany([
36+
YourInterface::class,
37+
YourSecondIntfaceOrTrait::class,
38+
])->save()
39+
```

composer.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "e2d2-dev/modify-classes",
3+
"description": "",
4+
"type": "library",
5+
"license": "MIT",
6+
"require": {},
7+
"autoload": {
8+
"psr-4": {
9+
"Betta\\ModifyClasses\\": "src/"
10+
}
11+
},
12+
"minimum-stability": "stable"
13+
}

src/Concerns/CanInject.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns;
4+
5+
trait CanInject
6+
{
7+
use HasInjectables;
8+
9+
public function addMany(array $injectables): static
10+
{
11+
foreach ($injectables as $injectable) {
12+
$this->add($injectable);
13+
}
14+
return $this;
15+
}
16+
17+
public function add(string $injectable): static
18+
{
19+
$this->injectable = $injectable;
20+
$this->injectableType();
21+
$this->handleInjectable();
22+
$this->reset();
23+
24+
return $this;
25+
}
26+
27+
protected function injectAfter($needle, $inject): void
28+
{
29+
$this->buffer = str($this->buffer)
30+
->before($needle)
31+
->append($needle)
32+
->append($inject)
33+
->append(
34+
str($this->buffer)->after($needle)
35+
);
36+
}
37+
38+
protected function handleInjectable(): void
39+
{
40+
match ($this->getInjectableType()) {
41+
'interface' => $this->injectInterface(),
42+
'trait' => $this->injectTrait(),
43+
};
44+
}
45+
46+
protected function injectUse(): void
47+
{
48+
$use = "use {$this->getInjectableName()}";
49+
50+
if ($this->buffer->contains($use)) {
51+
$name = $this->getInjectableName();
52+
$replace = $this->buffer
53+
->after($this->getRawDescriptor()->toString())
54+
->before($name)
55+
->afterLast("\n")
56+
->append($name)
57+
->toString();
58+
59+
$this->buffer = $this->buffer->replace($replace, "\t$use");
60+
61+
return;
62+
}
63+
64+
$replace = $this->buffer->after(
65+
$this->getRawDescriptor()->toString()
66+
)->after('{');
67+
68+
$this->buffer = $this->buffer->replace($replace, $replace->prepend("\n\t$use;"));
69+
}
70+
}

src/Concerns/CanRemove.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns;
4+
5+
trait CanRemove
6+
{
7+
protected ?string $removeable = null;
8+
protected ?string $removeableType = null;
9+
10+
public function removeMany(array $injectables): static
11+
{
12+
foreach ($injectables as $injectable) {
13+
$this->remove($injectable);
14+
}
15+
return $this;
16+
}
17+
18+
public function remove(string $removeable): static
19+
{
20+
$this->removeable = $removeable;
21+
$this->removeableType();
22+
$this->handleRemoveable();
23+
$this->reset();
24+
25+
return $this;
26+
}
27+
28+
protected function getRemoveable(): ?string
29+
{
30+
return $this->removeable;
31+
}
32+
33+
protected function getRemoveableName(): string
34+
{
35+
return class_basename($this->getRemoveable());
36+
}
37+
38+
protected function removeInterface(): void
39+
{
40+
if (! $this->hasInterface()) {
41+
return;
42+
}
43+
$this->injectDescriptor();
44+
$this->removeImport($this->getRemoveable());
45+
}
46+
47+
protected function hasInterface(): bool
48+
{
49+
return $this->getImplements()->filter(fn ($interface) => $interface === $this->getRemoveable())->isNotEmpty();
50+
}
51+
52+
protected function removeTrait(): void
53+
{
54+
$this->removeImport($this->getRemoveable());
55+
$this->removeUse();
56+
}
57+
58+
protected function removeUse(): void
59+
{
60+
collect([
61+
"use {$this->getRemoveable()};",
62+
"use {$this->getRemoveableName()};",
63+
])->each(fn ($line) => $this->buffer = $this->buffer->remove($line));
64+
}
65+
66+
protected function handleRemoveable(): void
67+
{
68+
match ($this->getRemoveableType()) {
69+
'interface' => $this->removeInterface(),
70+
'trait' => $this->removeTrait(),
71+
};
72+
}
73+
74+
public function getRemoveableType(): ?string
75+
{
76+
return $this->removeableType;
77+
}
78+
79+
protected function removeableType(): void
80+
{
81+
$this->removeableType = $this->getType($this->removeable);
82+
}
83+
}

src/Concerns/CanReset.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns;
4+
5+
trait CanReset
6+
{
7+
protected function reset(): void
8+
{
9+
$this->injectable = null;
10+
$this->injectableType = null;
11+
$this->removeable = null;
12+
$this->removeableType = null;
13+
}
14+
}

src/Concerns/Class/CanExtend.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns\Class;
4+
5+
trait CanExtend
6+
{
7+
protected ?string $extendsName;
8+
9+
protected ?string $extendsClass;
10+
11+
protected function getExtendsName(): ?string
12+
{
13+
return $this->extendsName ??= ! $this->classExtends()
14+
? null
15+
: $this->getDescriptor()
16+
->after('extends')
17+
->before('implements')
18+
->trim();
19+
}
20+
21+
protected function classExtends(): ?string
22+
{
23+
return $this->getDescriptor()->contains('extends');
24+
}
25+
26+
protected function getExtendsClass(): ?string
27+
{
28+
return $this->extendsClass ??= $this->getImportedClass($this->getExtendsName());
29+
}
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns\Class;
4+
5+
use Illuminate\Support\Collection;
6+
7+
trait CanImplement
8+
{
9+
protected Collection $implements;
10+
11+
protected function getImplements(): Collection
12+
{
13+
return $this->implements ??= ! $this->getDescriptor()->contains('implements')
14+
? collect() :
15+
$this->getDescriptor()
16+
->after('implements')
17+
->trim()
18+
->remove(' ')
19+
->explode(',')
20+
->mapWithKeys(fn (string $item) => [$item => $this->getImportedClass($item)]);
21+
}
22+
23+
protected function hasImplements(): bool
24+
{
25+
return $this->getImplements()->isNotEmpty();
26+
}
27+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns\Class;
4+
5+
use Illuminate\Support\Collection;
6+
use Illuminate\Support\Stringable;
7+
8+
trait HasDescriptor
9+
{
10+
protected Stringable $descriptor;
11+
12+
protected function getDescriptor(): Stringable
13+
{
14+
return $this->descriptor ??= $this->getRawDescriptor()
15+
->before('//')
16+
->before('/*')
17+
->trim()
18+
->trim("\n")
19+
->trim("\t");
20+
}
21+
22+
protected function getRawDescriptor(): Stringable
23+
{
24+
return $this->buffer
25+
->after("class {$this->getClassBasename()}")
26+
->ltrim()
27+
->before("\n{");
28+
}
29+
30+
protected function injectDescriptor(): void
31+
{
32+
$replace = $this->getRawDescriptor();
33+
34+
if ($replace->contains('//')) {
35+
$replace = $replace->before('//')->rtrim();
36+
}
37+
38+
$this->buffer = $this->buffer->replace(
39+
$replace->toString(),
40+
$this->constructDescriptor()
41+
);
42+
}
43+
44+
protected function constructDescriptor(): string
45+
{
46+
$result = [];
47+
48+
if ($this->getExtendsName()) {
49+
$result[] = "extends {$this->getExtendsName()}";
50+
}
51+
52+
$implements = $this->constructImplements();
53+
54+
if ($implements->count()) {
55+
$result[] = 'implements '.$implements->implode(', ');
56+
}
57+
58+
return implode(' ', $result);
59+
}
60+
61+
protected function constructImplements(): Collection
62+
{
63+
return $this->getImplements()
64+
->reject(fn ($interface) => $interface === $this->getRemoveable())
65+
->flip()
66+
->push(
67+
class_basename($this->getInjectable())
68+
)
69+
->filter()
70+
->values()
71+
->unique();
72+
}
73+
}

src/Concerns/Class/HasImported.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Betta\ModifyClasses\Concerns\Class;
4+
5+
use Illuminate\Support\Stringable;
6+
7+
trait HasImported
8+
{
9+
protected function getClassImportLine(string $name): Stringable
10+
{
11+
return $this->buffer
12+
->after($this->getClassNamespace().';')
13+
->trim()
14+
->before('class ')
15+
->beforeLast("\n")
16+
->explode('use ')
17+
->filter(fn ($line) => str($line)->afterLast('\\')->contains($name))
18+
->map(fn ($line) => str($line)->remove(';')->trim())
19+
->first();
20+
}
21+
22+
protected function getImportedClass(string $name): string
23+
{
24+
return match (true) {
25+
class_exists($name) => $name,
26+
$this->importedIsAlias($name) => $this->getClassImportLine($name)->before(' as ')->trim()->toString(),
27+
default => $this->getClassImportLine($name)->toString(),
28+
};
29+
}
30+
31+
protected function importedIsAlias(string $name): bool
32+
{
33+
return $this->getClassImportLine($name)->contains(' as ');
34+
}
35+
}

0 commit comments

Comments
 (0)