|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* This file is part of the Kdyby (http://www.kdyby.org) |
|
5
|
|
|
* |
|
6
|
|
|
* Copyright (c) 2008 Filip Procházka ([email protected]) |
|
7
|
|
|
* |
|
8
|
|
|
* For the full copyright and license information, please view the file license.txt that was distributed with this source code. |
|
9
|
|
|
*/ |
|
10
|
|
|
|
|
11
|
|
|
namespace Kdyby\Doctrine\Diagnostics; |
|
12
|
|
|
|
|
13
|
|
|
use Doctrine; |
|
14
|
|
|
use Kdyby; |
|
15
|
|
|
use Tracy\Debugger; |
|
16
|
|
|
use Tracy\Dumper; |
|
17
|
|
|
use Tracy\Helpers; |
|
18
|
|
|
|
|
19
|
|
|
|
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* @author Filip Procházka <[email protected]> |
|
23
|
|
|
*/ |
|
24
|
|
|
class EntityManagerUnitOfWorkSnapshotPanel |
|
25
|
|
|
{ |
|
26
|
|
|
|
|
27
|
|
|
use \Kdyby\StrictObjects\Scream; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* @var \Doctrine\ORM\EntityManager |
|
31
|
|
|
*/ |
|
32
|
|
|
private $em; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* @var \Throwable[] |
|
36
|
|
|
*/ |
|
37
|
|
|
private $whitelistExceptions = []; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* @var \Doctrine\ORM\UnitOfWork|NULL |
|
41
|
|
|
*/ |
|
42
|
|
|
private $unitOfWorkSnapshot; |
|
43
|
|
|
|
|
44
|
|
|
public function markExceptionOwner(Doctrine\ORM\EntityManager $em, $exception) |
|
45
|
|
|
{ |
|
46
|
|
|
if ($this->em !== $em) { |
|
47
|
|
|
return; |
|
48
|
|
|
} |
|
49
|
|
|
|
|
50
|
|
|
$this->whitelistExceptions[] = $exception; |
|
51
|
|
|
} |
|
52
|
|
|
|
|
53
|
|
|
public function snapshotUnitOfWork(Doctrine\ORM\EntityManager $em) |
|
54
|
|
|
{ |
|
55
|
|
|
if ($this->em !== $em) { |
|
56
|
|
|
return; |
|
57
|
|
|
} |
|
58
|
|
|
|
|
59
|
|
|
$this->unitOfWorkSnapshot = clone $em->getUnitOfWork(); |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* @param \Exception|\Throwable $e |
|
64
|
|
|
* @return array|NULL |
|
65
|
|
|
*/ |
|
66
|
|
|
public function renderEntityManagerException($e) |
|
67
|
|
|
{ |
|
68
|
|
|
if (!in_array($e, $this->whitelistExceptions, TRUE)) { |
|
69
|
|
|
return NULL; // ignore |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
if (strpos(get_class($e), 'Doctrine\\ORM\\') !== FALSE && Helpers::findTrace($e->getTrace(), Doctrine\ORM\EntityManager::class . '::flush')) { |
|
73
|
|
|
$UoW = $this->unitOfWorkSnapshot ?: $this->em->getUnitOfWork(); |
|
74
|
|
|
|
|
75
|
|
|
$panel = '<div class="inner"><p><b>IdentityMap</b></p>' . |
|
76
|
|
|
Dumper::toHtml($UoW->getIdentityMap(), [Dumper::COLLAPSE => TRUE]) . |
|
77
|
|
|
'</div>'; |
|
78
|
|
|
|
|
79
|
|
|
if ($scheduled = $UoW->getScheduledEntityInsertions()) { |
|
80
|
|
|
$panel .= '<div class="inner"><p><b>Scheduled entity insertions</b></p>' . |
|
81
|
|
|
Dumper::toHtml($scheduled, [Dumper::COLLAPSE => TRUE]) . |
|
82
|
|
|
'</div>'; |
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
if ($scheduled = $UoW->getScheduledEntityDeletions()) { |
|
86
|
|
|
$panel .= '<div class="inner"><p><b>Scheduled entity deletions</b></p>' . |
|
87
|
|
|
Dumper::toHtml($scheduled, [Dumper::COLLAPSE => TRUE]) . |
|
88
|
|
|
'</div>'; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
if ($scheduled = $UoW->getScheduledEntityUpdates()) { |
|
92
|
|
|
$panel .= '<div class="inner"><p><b>Scheduled entity updates</b></p>' . |
|
93
|
|
|
Dumper::toHtml($scheduled, [Dumper::COLLAPSE => TRUE]) . |
|
94
|
|
|
'</div>'; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
return [ |
|
98
|
|
|
'tab' => Doctrine\ORM\UnitOfWork::class, |
|
99
|
|
|
'panel' => $panel, |
|
100
|
|
|
]; |
|
101
|
|
|
} |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* @param Doctrine\ORM\EntityManager $em |
|
106
|
|
|
* @return Panel |
|
107
|
|
|
*/ |
|
108
|
|
|
public function bindEntityManager(Doctrine\ORM\EntityManager $em) |
|
|
|
|
|
|
109
|
|
|
{ |
|
110
|
|
|
if ($this->em !== NULL) { |
|
111
|
|
|
throw new Kdyby\Doctrine\InvalidStateException(sprintf('%s is already bound to an entity manager.', __CLASS__)); |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
$this->em = $em; |
|
115
|
|
|
if ($this->em instanceof Kdyby\Doctrine\EntityManager) { |
|
116
|
|
|
$this->em->bindTracyPanel($this); |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
Debugger::getBlueScreen()->addPanel([$this, 'renderEntityManagerException']); |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
} |
|
123
|
|
|
|
The
EntityManagermight become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManageris closed. Any other code which depends on the same instance of theEntityManagerduring this request will fail.On the other hand, if you instead inject the
ManagerRegistry, thegetManager()method guarantees that you will always get a usable manager instance.