EntityManagerUnitOfWorkSnapshotPanel   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 15
c 0
b 0
f 0
lcom 1
cbo 9
dl 0
loc 99
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A markExceptionOwner() 0 8 2
A snapshotUnitOfWork() 0 8 2
B renderEntityManagerException() 0 37 8
A bindEntityManager() 0 13 3
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)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $em. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might 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:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
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