1: <?php
2: /**
3: * This class handles the db schema.
4: *
5: * PHP version 5
6: *
7: * @category Kolab
8: * @package Kolab_Server
9: * @author Gunnar Wrobel <wrobel@pardus.de>
10: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
11: * @link http://pear.horde.org/index.php?package=Kolab_Server
12: */
13:
14: /**
15: * This class handles the db schema.
16: *
17: * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
18: *
19: * See the enclosed file COPYING for license information (LGPL). If you
20: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
21: *
22: * @category Kolab
23: * @package Kolab_Server
24: * @author Gunnar Wrobel <wrobel@pardus.de>
25: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
26: * @link http://pear.horde.org/index.php?package=Kolab_Server
27: */
28: class Horde_Kolab_Server_Schema_Decorator_Cache
29: implements Horde_Kolab_Server_Schema_Interface
30: {
31: /** Maximum accepted level for the object class hierarchy */
32: const MAX_HIERARCHY = 100;
33:
34: /**
35: * A cache for object attribute definitions.
36: *
37: * @var array
38: */
39: protected $attributes;
40:
41: /**
42: * A link to the composite server handler.
43: *
44: * @var Horde_Kolab_Server_Composite
45: */
46: protected $composite;
47:
48: /**
49: * Set the composite server reference for this object.
50: *
51: * @param Horde_Kolab_Server_Composite $composite A link to the composite
52: * server handler.
53: *
54: * @return NULL
55: */
56: public function setComposite(
57: Horde_Kolab_Server_Composite $composite
58: ) {
59: $this->composite = $composite;
60: }
61:
62: /**
63: * Return the schema for the given objectClass.
64: *
65: * @param string $objectclass Fetch the schema for this objectClass.
66: *
67: * @return array The schema for the given objectClass.
68: *
69: * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
70: */
71: public function getObjectclassSchema($objectclass)
72: {
73: if (!empty($this->_config['schema_support'])) {
74: $schema = $this->_getSchema();
75: $info = $schema->get('objectclass', $objectclass);
76: $this->_handleError($info, Horde_Kolab_Server_Exception::SYSTEM);
77: return $info;
78: }
79: return parent::getObjectclassSchema($objectclass);
80: }
81:
82: /**
83: * Return the schema for the given attribute.
84: *
85: * @param string $attribute Fetch the schema for this attribute.
86: *
87: * @return array The schema for the given attribute.
88: *
89: * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
90: */
91: public function getAttributeSchema($attribute)
92: {
93: if (!empty($this->_config['schema_support'])) {
94: $schema = $this->_getSchema();
95: $info = $schema->get('attribute', $attribute);
96: $this->_handleError($info, Horde_Kolab_Server_Exception::SYSTEM);
97: return $info;
98: }
99: return parent::getAttributeSchema($attribute);
100: }
101:
102: /**
103: * Return the attributes supported by the given object class.
104: *
105: * @param string $class Determine the attributes for this class.
106: *
107: * @return array The supported attributes.
108: *
109: * @throws Horde_Kolab_Server_Exception If the schema analysis fails.
110: */
111: public function getExternalAttributes($class)
112: {
113: if (!isset($this->attributes)) {
114: if (isset($this->cache)) {
115: register_shutdown_function(array($this, 'shutdown'));
116: }
117: }
118: if (empty($this->attributes[$class])) {
119:
120: if (isset($this->cache)) {
121: $this->attributes[$class] = @unserialize($cache->get('attributes_' . $class,
122: $this->params['cache_lifetime']));
123: }
124:
125: if (empty($this->attributes[$class])) {
126:
127: $childclass = $class;
128: $classes = array();
129: $level = 0;
130: while ($childclass != 'Horde_Kolab_Server_Object'
131: && $level < self::MAX_HIERARCHY) {
132: $classes[] = $childclass;
133: $childclass = get_parent_class($childclass);
134: $level++;
135: }
136:
137: /** Finally add the basic object class */
138: $classes[] = $childclass;
139:
140: if ($level == self::MAX_HIERARCHY) {
141: if (isset($this->logger)) {
142: $logger->err(sprintf('The maximal level of the object hierarchy has been exceeded for class \"%s\"!',
143: $class));
144: }
145: }
146:
147: /**
148: * Collect attributes from bottom to top.
149: */
150: $classes = array_reverse($classes);
151:
152: $types = array('defined', 'required', 'derived', 'collapsed',
153: 'defaults', 'locked', 'object_classes');
154: foreach ($types as $type) {
155: $$type = array();
156: }
157:
158: foreach ($classes as $childclass) {
159: $vars = get_class_vars($childclass);
160: if (isset($vars['init_attributes'])) {
161: foreach ($types as $type) {
162: /**
163: * If the user wishes to adhere to the schema
164: * information from the server we will skip the
165: * attributes defined within the object class here.
166: */
167: if (!empty($this->params['schema_override'])
168: && in_array($type, 'defined', 'required')) {
169: continue;
170: }
171: if (isset($vars['init_attributes'][$type])) {
172: $$type = array_merge($$type,
173: $vars['init_attributes'][$type]);
174: }
175: }
176: }
177: }
178:
179: $attrs = array();
180:
181: foreach ($object_classes as $object_class) {
182: $info = $this->getObjectclassSchema($object_class);
183: if (isset($info['may'])) {
184: $defined = array_merge($defined, $info['may']);
185: }
186: if (isset($info['must'])) {
187: $defined = array_merge($defined, $info['must']);
188: $required = array_merge($required, $info['must']);
189: }
190: foreach ($defined as $attribute) {
191: try {
192: $attrs[$attribute] = $this->getAttributeSchema($attribute);
193: } catch (Horde_Kolab_Server_Exception $e) {
194: /**
195: * If the server considers the attribute to be
196: * invalid we mark it.
197: */
198: $attrs[$attribute] = array('invalid' => true);
199: }
200: }
201: foreach ($required as $attribute) {
202: $attrs[$attribute]['required'] = true;
203: }
204: foreach ($locked as $attribute) {
205: $attrs[$attribute]['locked'] = true;
206: }
207: foreach ($defaults as $attribute => $default) {
208: $attrs[$attribute]['default'] = $default;
209: }
210: $attrs[Horde_Kolab_Server_Object::ATTRIBUTE_OC]['default'] = $object_classes;
211: }
212: foreach ($derived as $key => $attributes) {
213: $supported = true;
214: if (isset($attributes['base'])) {
215: foreach ($attributes['base'] as $attribute) {
216: /**
217: * Usually derived attribute are determined on basis
218: * of one or more attributes. If any of these is not
219: * supported the derived attribute should not be
220: * included into the set of supported attributes.
221: */
222: if (!isset($attrs[$attribute])) {
223: unset($derived[$attribute]);
224: $supported = false;
225: break;
226: }
227: }
228: }
229: if ($supported) {
230: $attrs[$key] = $attributes;
231: }
232: }
233: $check_collapsed = $collapsed;
234: foreach ($check_collapsed as $key => $attributes) {
235: if (isset($attributes['base'])) {
236: foreach ($attributes['base'] as $attribute) {
237: /**
238: * Usually collapsed attribute are determined on basis
239: * of one or more attributes. If any of these is not
240: * supported the collapsed attribute should not be
241: * included into the set of supported attributes.
242: */
243: if (!isset($attrs[$attribute])) {
244: unset($collapsed[$attribute]);
245: }
246: }
247: }
248: }
249: $this->attributes[$class] = array($attrs,
250: array(
251: 'derived' => array_keys($derived),
252: 'collapsed' => $collapsed,
253: 'locked' => $locked,
254: 'required' => $required));
255: }
256: }
257: return $this->attributes[$class];
258: }
259:
260: public function getInternalAttributes($class)
261: {
262: }
263:
264: /**
265: * Stores the attribute definitions in the cache.
266: *
267: * @return Horde_Kolab_Server The concrete Horde_Kolab_Server reference.
268: */
269: public function shutdown()
270: {
271: if (isset($this->attributes)) {
272: if (isset($this->cache)) {
273: foreach ($this->attributes as $key => $value) {
274: $this->cache->set('attributes_' . $key, @serialize($value));
275: }
276: }
277: }
278: }
279:
280:
281: }