Passed
Push — master ( 7f11bc...601709 )
by Andreas
12:40
created

get_component_classes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.services
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use Doctrine\Common\Util\ClassUtils;
10
11
/**
12
 * <b>How to write database class definitions:</b>
13
 *
14
 * The general idea is to provide MidCOM with a way to hook into every database interaction
15
 * between the component and the Midgard core.
16
 *
17
 * Since PHP does not allow for multiple inheritance (which would be really useful here),
18
 * a decorator pattern is used, which connects the class you actually use in your component
19
 * and the original MgdSchema class while at the same time routing all function calls through
20
 * midcom_core_dbaobject.
21
 *
22
 * The class loader does not require much information when registering classes:
23
 * An example declaration looks like this:
24
 *
25
 * <code>
26
 * [
27
 *     'midgard_article' => 'midcom_db_article'
28
 * ]
29
 * </code>
30
 *
31
 * The key is the MgdSchema class name from that you want to use. The class specified must exist.
32
 *
33
 * The value is the name of the MidCOM base class you intend to create.
34
 * It is checked for basic validity against the PHP restrictions on symbol naming, but the
35
 * class itself is not checked for existence. You <i>must</i> declare the class as listed at
36
 * all times, as typecasting and detection is done using this metadata property in the core.
37
 *
38
 * <b>Inherited class requirements</b>
39
 *
40
 * The classes you inherit from the intermediate stub classes must at this time satisfy one
41
 * requirement: You have to declare the midcom and mgdschema classnames:
42
 *
43
 * <code>
44
 * class midcom_db_article
45
 *     extends midcom_core_dbaobject
46
 * {
47
 *      public string $__midcom_class_name__ = __CLASS__;
48
 *      public string $__mgdschema_class_name__ = 'midgard_article';
49
 *
50
 * </code>
51
 *
52
 * @package midcom.services
53
 */
54
class midcom_services_dbclassloader
55
{
56
    private array $map;
57
58 2
    public function __construct(array $map)
59
    {
60 2
        $this->map = $map;
61
    }
62
63
    /**
64
     * Get component name associated with a class name to get its DBA classes defined
65
     */
66 31
    public function get_component_for_class(string $classname) : ?string
67
    {
68 31
        $class_parts = array_filter(explode('_', $classname));
69
        // Fix for incorrectly named classes
70 31
        $component_map = [
71 31
            'midgard' => 'midcom',
72 31
            'openpsa' => 'midcom', // for openpsa_person class
73 31
            'org.openpsa.campaign' => 'org.openpsa.directmarketing',
74 31
            'org.openpsa.link' => 'org.openpsa.directmarketing',
75 31
            'org.openpsa.document' => 'org.openpsa.documents',
76 31
            'org.openpsa.organization' => 'org.openpsa.contacts',
77 31
            'org.openpsa.person' => 'org.openpsa.contacts',
78 31
            'org.openpsa.member' => 'org.openpsa.contacts',
79 31
            'org.openpsa.salesproject' => 'org.openpsa.sales',
80 31
            'org.openpsa.offer' => 'org.openpsa.sales',
81 31
            'org.openpsa.event' => 'org.openpsa.calendar',
82 31
            'org.openpsa.eventmember' => 'org.openpsa.calendar',
83 31
            'org.openpsa.invoice' => 'org.openpsa.invoices',
84 31
            'org.openpsa.billing' => 'org.openpsa.invoices',
85 31
            'org.openpsa.query' => 'org.openpsa.reports',
86 31
            'org.openpsa.task' => 'org.openpsa.projects',
87 31
            'org.openpsa.project' => 'org.openpsa.projects',
88 31
            'org.openpsa.role' => 'org.openpsa.projects',
89 31
            'org.openpsa.hour' => 'org.openpsa.expenses'
90 31
        ];
91
92 31
        while (!empty($class_parts)) {
93 31
            $component = implode('.', $class_parts);
94 31
            if (array_key_exists($component, $component_map)) {
95 22
                $component = $component_map[$component];
96
            }
97
98 31
            if (array_key_exists($component, $this->map)) {
99 31
                return $component;
100
            }
101 29
            array_pop($class_parts);
102
        }
103
104
        return null;
105
    }
106
107
    /**
108
     * Get a MidCOM DB class name for a MgdSchema Object.
109
     * We also ensure that the corresponding component has been loaded.
110
     */
111 323
    public function get_midcom_class_name_for_mgdschema_object(string|object $classname) : ?string
112
    {
113 323
        static $dba_classes_by_mgdschema = [];
114
115 323
        if (is_object($classname)) {
116 162
            $classname = ClassUtils::getClass($classname);
117
        }
118
119 323
        if (!array_key_exists($classname, $dba_classes_by_mgdschema)) {
120 11
            foreach ($this->map as $mapping) {
121 11
                if (array_key_exists($classname, $mapping)) {
122 11
                    $dba_classes_by_mgdschema[$classname] = $mapping[$classname];
123 11
                    break;
124
                }
125
            }
126 11
            if (!array_key_exists($classname, $dba_classes_by_mgdschema)) {
127
                debug_add("{$classname} cannot be resolved to any DBA class name");
128
                $dba_classes_by_mgdschema[$classname] = null;
129
            }
130
        }
131
132 323
        return $dba_classes_by_mgdschema[$classname];
133
    }
134
135
    /**
136
     * Get an MgdSchema class name for a MidCOM DBA class name
137
     *
138
     * @return string The corresponding MidCOM DBA class name, false otherwise.
139
     */
140 500
    public function get_mgdschema_class_name_for_midcom_class(string $classname)
141
    {
142 500
        static $mapping = [];
143
144 500
        if (!array_key_exists($classname, $mapping)) {
145 13
            $mapping[$classname] = false;
146
147 13
            if (class_exists($classname)) {
148 13
                if ($this->is_midcom_db_object($classname)) {
149 13
                    $mapping[$classname] = (new $classname)->__mgdschema_class_name__;
150
                }
151
            }
152
        }
153
154 500
        return $mapping[$classname];
155
    }
156
157
    /**
158
     * Simple helper to check whether we are dealing with a MidCOM Database object
159
     * or a subclass thereof.
160
     */
161 526
    public function is_midcom_db_object(object|string $object) : bool
162
    {
163 526
        return is_subclass_of($object, midcom_core_dbaobject::class)
164 526
            || is_a($object, midcom_core_dbaproxy::class, true);
165
    }
166
167 6
    public function get_component_classes(string $component) : array
168
    {
169 6
        return $this->map[$component];
170
    }
171
}
172