1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Savers; |
6
|
|
|
|
7
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
8
|
|
|
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface; |
9
|
|
|
use RuntimeException; |
10
|
|
|
|
11
|
|
|
abstract class AbstractBulkProcess |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* @var EntityManagerInterface |
15
|
|
|
*/ |
16
|
|
|
protected $entityManager; |
17
|
|
|
|
18
|
|
|
protected $entitiesToSave = []; |
19
|
|
|
|
20
|
|
|
protected $chunkSize = 1000; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var float |
24
|
|
|
*/ |
25
|
|
|
protected $secondsToPauseBetweenSaves = 0.0; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var bool |
29
|
|
|
*/ |
30
|
|
|
private $gcWasEnabled; |
31
|
|
|
|
32
|
|
|
private $started = false; |
33
|
|
|
private $ended = false; |
34
|
|
|
|
35
|
|
|
public function __construct(EntityManagerInterface $entityManager) |
36
|
|
|
{ |
37
|
|
|
$this->entityManager = $entityManager; |
38
|
|
|
$this->gcWasEnabled = gc_enabled(); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function __destruct() |
42
|
|
|
{ |
43
|
|
|
if (true === $this->started && false === $this->ended) { |
44
|
|
|
if (!$this->entityManager->isOpen()) { |
45
|
|
|
throw new RuntimeException('Error in ' . __METHOD__ . ': Entity Manager has been closed'); |
46
|
|
|
} |
47
|
|
|
$this->endBulkProcess(); |
48
|
|
|
} |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
public function endBulkProcess(): void |
52
|
|
|
{ |
53
|
|
|
$this->started = false; |
54
|
|
|
$this->ended = true; |
55
|
|
|
|
56
|
|
|
if ([] !== $this->entitiesToSave) { |
57
|
|
|
$this->doSave(); |
58
|
|
|
$this->freeResources(); |
59
|
|
|
} |
60
|
|
|
if (false === $this->gcWasEnabled) { |
61
|
|
|
return; |
62
|
|
|
} |
63
|
|
|
gc_enable(); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
abstract protected function doSave(): void; |
67
|
|
|
|
68
|
|
|
protected function freeResources() |
69
|
|
|
{ |
70
|
|
|
gc_enable(); |
71
|
|
|
foreach ($this->entitiesToSave as $entity) { |
72
|
|
|
$this->entityManager->detach($entity); |
|
|
|
|
73
|
|
|
} |
74
|
|
|
$this->entitiesToSave = []; |
75
|
|
|
gc_collect_cycles(); |
76
|
|
|
gc_disable(); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
public function addEntityToSave(EntityInterface $entity) |
80
|
|
|
{ |
81
|
|
|
if (false === $this->started) { |
82
|
|
|
$this->startBulkProcess(); |
83
|
|
|
} |
84
|
|
|
$this->entitiesToSave[] = $entity; |
85
|
|
|
$this->bulkSaveIfChunkBigEnough(); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
public function startBulkProcess(): self |
89
|
|
|
{ |
90
|
|
|
gc_disable(); |
91
|
|
|
$this->started = true; |
92
|
|
|
$this->ended = false; |
93
|
|
|
|
94
|
|
|
return $this; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
protected function bulkSaveIfChunkBigEnough(): void |
98
|
|
|
{ |
99
|
|
|
$size = count($this->entitiesToSave); |
100
|
|
|
if ($size >= $this->chunkSize) { |
101
|
|
|
$this->doSave(); |
102
|
|
|
$this->freeResources(); |
103
|
|
|
$this->pauseBetweenSaves(); |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* If configured, we will pause between starting another round of saves |
109
|
|
|
*/ |
110
|
|
|
private function pauseBetweenSaves(): void |
111
|
|
|
{ |
112
|
|
|
if (0 >= $this->secondsToPauseBetweenSaves) { |
113
|
|
|
return; |
114
|
|
|
} |
115
|
|
|
usleep((int)$this->secondsToPauseBetweenSaves * 1000000); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* This will prevent any notification on changed properties |
120
|
|
|
* |
121
|
|
|
* @param array|EntityInterface[] $entities |
122
|
|
|
* |
123
|
|
|
* @return $this |
124
|
|
|
*/ |
125
|
|
|
public function prepareEntitiesForBulkUpdate(array $entities): self |
126
|
|
|
{ |
127
|
|
|
foreach ($entities as $entity) { |
128
|
|
|
$entity->removePropertyChangedListeners(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
return $this; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
public function addEntitiesToSave(array $entities) |
135
|
|
|
{ |
136
|
|
|
$entitiesToSaveBackup = $this->entitiesToSave; |
137
|
|
|
$chunks = array_chunk($entities, $this->chunkSize, true); |
138
|
|
|
foreach ($chunks as $chunk) { |
139
|
|
|
$this->entitiesToSave = $chunk; |
140
|
|
|
$this->bulkSaveIfChunkBigEnough(); |
141
|
|
|
} |
142
|
|
|
$this->entitiesToSave = array_merge($this->entitiesToSave, $entitiesToSaveBackup); |
143
|
|
|
$this->bulkSaveIfChunkBigEnough(); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* @return int |
148
|
|
|
*/ |
149
|
|
|
public function getChunkSize(): int |
150
|
|
|
{ |
151
|
|
|
return $this->chunkSize; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* @param int $chunkSize |
156
|
|
|
* |
157
|
|
|
* @return $this |
158
|
|
|
*/ |
159
|
|
|
public function setChunkSize(int $chunkSize): self |
160
|
|
|
{ |
161
|
|
|
$this->chunkSize = $chunkSize; |
162
|
|
|
|
163
|
|
|
return $this; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @param float $secondsToPauseBetweenSaves |
168
|
|
|
*/ |
169
|
|
|
public function setSecondsToPauseBetweenSaves(float $secondsToPauseBetweenSaves): void |
170
|
|
|
{ |
171
|
|
|
$this->secondsToPauseBetweenSaves = $secondsToPauseBetweenSaves; |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.