Skip to content

Commit bf0cd91

Browse files
peterfoxGeniJahodriftingly
authored
Dispatch to DispatchSync with non queueable job (#182)
* Working dispatch to dispatch_sync rule * wip * Still failing * Working * correct docs * Docs update * Remove needing to be assigned --------- Co-authored-by: Geni Jaho <jahogeni@gmail.com> Co-authored-by: Anthony Clark <anthonyclark@gmail.com>
1 parent 47d2b04 commit bf0cd91

15 files changed

+372
-1
lines changed

config/sets/laravel100.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use RectorLaravel\Rector\Cast\DatabaseExpressionCastsToMethodCallRector;
1212
use RectorLaravel\Rector\Class_\ReplaceExpectsMethodsInTestsRector;
1313
use RectorLaravel\Rector\Class_\UnifyModelDatesWithCastsRector;
14+
use RectorLaravel\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector;
1415
use RectorLaravel\Rector\MethodCall\DatabaseExpressionToStringToMethodCallRector;
1516
use RectorLaravel\Rector\MethodCall\ReplaceWithoutJobsEventsAndNotificationsWithFacadeFakeRector;
1617
use RectorLaravel\Rector\StaticCall\ReplaceAssertTimesSendWithAssertSentTimesRector;
@@ -48,4 +49,8 @@
4849
// https://github.com/laravel/framework/pull/42591/files
4950
'dispatch_now' => 'dispatch_sync',
5051
]);
52+
53+
// https://laravel.com/docs/10.x/upgrade#dispatch-return
54+
$rectorConfig
55+
->rule(DispatchNonShouldQueueToDispatchSyncRector::class);
5156
};

docs/rector_rules_overview.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 54 Rules Overview
1+
# 56 Rules Overview
22

33
## AddArgumentDefaultValueRector
44

@@ -433,6 +433,23 @@ Convert DB Expression `__toString()` calls to `getValue()` method calls.
433433

434434
<br>
435435

436+
## DispatchNonShouldQueueToDispatchSyncRector
437+
438+
Dispatch non ShouldQueue jobs to dispatchSync
439+
440+
- class: [`RectorLaravel\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector`](../src/Rector/FuncCall/DispatchNonShouldQueueToDispatchSyncRector.php)
441+
442+
```diff
443+
-dispatch(new SomeJob());
444+
-Bus::dispatch(new SomeJob());
445+
-$this->dispatch(new SomeJob());
446+
+dispatch_sync(new SomeJob());
447+
+Bus::dispatchSync(new SomeJob());
448+
+$this->dispatchSync(new SomeJob());
449+
```
450+
451+
<br>
452+
436453
## EloquentMagicMethodToQueryBuilderRector
437454

438455
The EloquentMagicMethodToQueryBuilderRule is designed to automatically transform certain magic method calls on Eloquent Models into corresponding Query Builder method calls.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace RectorLaravel\Rector\FuncCall;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Arg;
7+
use PhpParser\Node\Expr\FuncCall;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Expr\StaticCall;
10+
use PhpParser\Node\Identifier;
11+
use PhpParser\Node\Name;
12+
use PHPStan\Broker\ClassNotFoundException;
13+
use PHPStan\Reflection\ClassReflection;
14+
use PHPStan\Reflection\ReflectionProvider;
15+
use PHPStan\Type\ObjectType;
16+
use Rector\Rector\AbstractRector;
17+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
18+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
19+
20+
/**
21+
* @see \RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\DispatchNonShouldQueueToDispatchSyncRectorTest
22+
*/
23+
class DispatchNonShouldQueueToDispatchSyncRector extends AbstractRector
24+
{
25+
public function __construct(private readonly ReflectionProvider $reflectionProvider)
26+
{
27+
}
28+
29+
public function getRuleDefinition(): RuleDefinition
30+
{
31+
return new RuleDefinition(
32+
'Dispatch non ShouldQueue jobs to dispatchSync',
33+
[
34+
new CodeSample(
35+
<<<'CODE_SAMPLE'
36+
dispatch(new SomeJob());
37+
Bus::dispatch(new SomeJob());
38+
$this->dispatch(new SomeJob());
39+
CODE_SAMPLE
40+
,
41+
<<<'CODE_SAMPLE'
42+
dispatch_sync(new SomeJob());
43+
Bus::dispatchSync(new SomeJob());
44+
$this->dispatchSync(new SomeJob());
45+
CODE_SAMPLE
46+
),
47+
]
48+
);
49+
}
50+
51+
public function getNodeTypes(): array
52+
{
53+
return [FuncCall::class, MethodCall::class, StaticCall::class];
54+
}
55+
56+
/**
57+
* @param FuncCall|MethodCall|StaticCall $node
58+
*/
59+
public function refactor(Node $node): FuncCall|MethodCall|StaticCall|null
60+
{
61+
if (
62+
$this->isName($node->name, 'dispatch') &&
63+
count($node->args) === 1
64+
) {
65+
if (
66+
$node instanceof MethodCall &&
67+
! $this->isDispatchablesCall($node) &&
68+
! $this->isCallOnDispatcherContract($node)
69+
) {
70+
return null;
71+
}
72+
73+
if (
74+
$node instanceof StaticCall
75+
&& ! $this->isCallOnBusFacade($node)
76+
) {
77+
return null;
78+
}
79+
80+
$newNode = $this->processCall($node);
81+
82+
if ($newNode !== null) {
83+
return $newNode;
84+
}
85+
}
86+
87+
return null;
88+
}
89+
90+
private function processCall(FuncCall|MethodCall|StaticCall $call): FuncCall|MethodCall|StaticCall|null
91+
{
92+
static $shouldQueueType = new ObjectType('Illuminate\Contracts\Queue\ShouldQueue');
93+
94+
if (! $call->args[0] instanceof Arg) {
95+
return null;
96+
}
97+
98+
if (
99+
$this->getType($call->args[0]->value)->isSuperTypeOf(
100+
$shouldQueueType
101+
)->yes() ||
102+
$this->isObjectType(
103+
$call->args[0]->value,
104+
$shouldQueueType
105+
)
106+
) {
107+
return null;
108+
}
109+
110+
$call->name = $call instanceof FuncCall ? new Name('dispatch_sync') : new Identifier('dispatchSync');
111+
112+
return $call;
113+
}
114+
115+
private function isDispatchablesCall(MethodCall $methodCall): bool
116+
{
117+
$type = $this->getType($methodCall->var);
118+
if (! $type instanceof ObjectType) {
119+
return false;
120+
}
121+
122+
try {
123+
// Will trigger ClassNotFoundException if the class definition is not found
124+
$reflection = $this->reflectionProvider->getClass(
125+
$type->getClassName()
126+
);
127+
128+
if ($this->usesDispatchablesTrait($reflection)) {
129+
return true;
130+
}
131+
132+
} catch (ClassNotFoundException) {
133+
}
134+
135+
return false;
136+
}
137+
138+
private function usesDispatchablesTrait(ClassReflection $classReflection): bool
139+
{
140+
return in_array(
141+
'Illuminate\Foundation\Bus\Dispatchable',
142+
array_keys($classReflection->getTraits(true)),
143+
true
144+
);
145+
}
146+
147+
private function isCallOnBusFacade(StaticCall $staticCall): bool
148+
{
149+
return $this->isName($staticCall->class, 'Illuminate\Support\Facades\Bus');
150+
}
151+
152+
private function isCallOnDispatcherContract(MethodCall $methodCall): bool
153+
{
154+
return $this->isObjectType($methodCall->var, new ObjectType('Illuminate\Contracts\Bus\Dispatcher'));
155+
}
156+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class DispatchNonShouldQueueToDispatchSyncRectorTest extends AbstractRectorTestCase
12+
{
13+
public static function provideData(): Iterator
14+
{
15+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
16+
}
17+
18+
/**
19+
* @test
20+
*/
21+
#[DataProvider('provideData')]
22+
public function test(string $filePath): void
23+
{
24+
$this->doTestFile($filePath);
25+
}
26+
27+
public function provideConfigFilePath(): string
28+
{
29+
return __DIR__ . '/config/configured_rule.php';
30+
}
31+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
4+
5+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
6+
7+
\Illuminate\Support\Facades\Bus::dispatch(new SomeJob());
8+
9+
?>
10+
-----
11+
<?php
12+
13+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
14+
15+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
16+
17+
\Illuminate\Support\Facades\Bus::dispatchSync(new SomeJob());
18+
19+
?>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
4+
5+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\ClassWithDispatchableTrait;
6+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
7+
8+
(new ClassWithDispatchableTrait())->dispatch(new SomeJob());
9+
10+
?>
11+
-----
12+
<?php
13+
14+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
15+
16+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\ClassWithDispatchableTrait;
17+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
18+
19+
(new ClassWithDispatchableTrait())->dispatchSync(new SomeJob());
20+
21+
?>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
4+
5+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
6+
7+
class SomeClass
8+
{
9+
public function run(\Illuminate\Contracts\Bus\Dispatcher $dispatcher)
10+
{
11+
$dispatcher->dispatch(new SomeJob());
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
20+
21+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
22+
23+
class SomeClass
24+
{
25+
public function run(\Illuminate\Contracts\Bus\Dispatcher $dispatcher)
26+
{
27+
$dispatcher->dispatchSync(new SomeJob());
28+
}
29+
}
30+
31+
?>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
4+
5+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
6+
7+
dispatch(new SomeJob());
8+
9+
$job = new SomeJob();
10+
dispatch($job);
11+
12+
?>
13+
-----
14+
<?php
15+
16+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
17+
18+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
19+
20+
dispatch_sync(new SomeJob());
21+
22+
$job = new SomeJob();
23+
dispatch_sync($job);
24+
25+
?>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
4+
5+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
6+
7+
(new FooClass())->dispatch(new SomeJob());
8+
9+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Fixture;
4+
5+
use RectorLaravel\Tests\Rector\FuncCall\DispatchNonShouldQueueToDispatchSyncRector\Source\SomeJob;
6+
7+
SomeFacade::dispatch(new SomeJob());
8+
9+
?>

0 commit comments

Comments
 (0)