1<?php
2/**
3 * $Header$
4 * $Horde: horde/lib/Log/composite.php,v 1.2 2000/06/28 21:36:13 jon Exp $
5 *
6 * @version $Revision$
7 * @package Log
8 */
9
10/**
11 * The Log_composite:: class implements a Composite pattern which
12 * allows multiple Log implementations to receive the same events.
13 *
14 * @author  Chuck Hagenbuch <chuck@horde.org>
15 * @author  Jon Parise <jon@php.net>
16 *
17 * @since Horde 1.3
18 * @since Log 1.0
19 * @package Log
20 *
21 * @example composite.php   Using the composite handler.
22 */
23class Log_composite extends Log
24{
25    /**
26     * Array holding all of the Log instances to which log events should be
27     * sent.
28     *
29     * @var array
30     * @access private
31     */
32    var $_children = array();
33
34
35    /**
36     * Constructs a new composite Log object.
37     *
38     * @param string   $name       This parameter is ignored.
39     * @param string   $ident      This parameter is ignored.
40     * @param array    $conf       This parameter is ignored.
41     * @param int      $level      This parameter is ignored.
42     *
43     * @access public
44     */
45    public function __construct($name, $ident = '', $conf = array(),
46                                $level = PEAR_LOG_DEBUG)
47    {
48        $this->_ident = $ident;
49    }
50
51    /**
52     * Opens all of the child instances.
53     *
54     * @return  True if all of the child instances were successfully opened.
55     *
56     * @access public
57     */
58    function open()
59    {
60        /* Attempt to open each of our children. */
61        $this->_opened = true;
62        foreach ($this->_children as $child) {
63            $this->_opened &= $child->open();
64        }
65
66        /* If all children were opened, return success. */
67        return $this->_opened;
68    }
69
70    /**
71     * Closes all open child instances.
72     *
73     * @return  True if all of the opened child instances were successfully
74     *          closed.
75     *
76     * @access public
77     */
78    function close()
79    {
80        /* If we haven't been opened, there's nothing more to do. */
81        if (!$this->_opened) {
82            return true;
83        }
84
85        /* Attempt to close each of our children. */
86        $closed = true;
87        foreach ($this->_children as $child) {
88            if ($child->_opened) {
89                $closed &= $child->close();
90            }
91        }
92
93        /* Clear the opened state for consistency. */
94        $this->_opened = false;
95
96        /* If all children were closed, return success. */
97        return $closed;
98    }
99
100    /**
101     * Flushes all child instances.  It is assumed that all of the children
102     * have been successfully opened.
103     *
104     * @return  True if all of the child instances were successfully flushed.
105     *
106     * @access public
107     * @since Log 1.8.2
108     */
109    function flush()
110    {
111        /* Attempt to flush each of our children. */
112        $flushed = true;
113        foreach ($this->_children as $child) {
114            $flushed &= $child->flush();
115        }
116
117        /* If all children were flushed, return success. */
118        return $flushed;
119    }
120
121    /**
122     * Sends $message and $priority to each child of this composite.  If the
123     * appropriate children aren't already open, they will be opened here.
124     *
125     * @param mixed     $message    String or object containing the message
126     *                              to log.
127     * @param string    $priority   (optional) The priority of the message.
128     *                              Valid values are: PEAR_LOG_EMERG,
129     *                              PEAR_LOG_ALERT, PEAR_LOG_CRIT,
130     *                              PEAR_LOG_ERR, PEAR_LOG_WARNING,
131     *                              PEAR_LOG_NOTICE, PEAR_LOG_INFO, and
132     *                              PEAR_LOG_DEBUG.
133     *
134     * @return boolean  True if the entry is successfully logged.
135     *
136     * @access public
137     */
138    function log($message, $priority = null)
139    {
140        /* If a priority hasn't been specified, use the default value. */
141        if ($priority === null) {
142            $priority = $this->_priority;
143        }
144
145        /*
146         * Abort early if the priority is above the composite handler's
147         * maximum logging level.
148         *
149         * XXX: Consider whether or not introducing this change would break
150         * backwards compatibility.  Some users may be expecting composite
151         * handlers to pass on all events to their children regardless of
152         * their own priority.
153         */
154        #if (!$this->_isMasked($priority)) {
155        #    return false;
156        #}
157
158        /*
159         * Iterate over all of our children.  If a unopened child will respond
160         * to this log event, we attempt to open it immediately.  The composite
161         * handler's opened state will be enabled as soon as the first child
162         * handler is successfully opened.
163         *
164         * We track an overall success state that indicates whether or not all
165         * of the relevant child handlers were opened and successfully logged
166         * the event.  If one handler fails, we still attempt any remaining
167         * children, but we consider the overall result a failure.
168         */
169        $success = true;
170        foreach ($this->_children as $child) {
171            /* If this child won't respond to this event, skip it. */
172            if (!$child->_isMasked($priority)) {
173                continue;
174            }
175
176            /* If this child has yet to be opened, attempt to do so now. */
177            if (!$child->_opened) {
178                $success &= $child->open();
179
180                /*
181                 * If we've successfully opened our first handler, the
182                 * composite handler itself is considered to be opened.
183                 */
184                if (!$this->_opened && $success) {
185                    $this->_opened = true;
186                }
187            }
188
189            /* Finally, attempt to log the message to the child handler. */
190            if ($child->_opened) {
191                $success &= $child->log($message, $priority);
192            }
193        }
194
195        /* Notify the observers. */
196        $this->_announce(array('priority' => $priority, 'message' => $message));
197
198        /* Return success if all of the open children logged the event. */
199        return $success;
200    }
201
202    /**
203     * Returns true if this is a composite.
204     *
205     * @return boolean  True if this is a composite class.
206     *
207     * @access public
208     */
209    function isComposite()
210    {
211        return true;
212    }
213
214    /**
215     * Sets this identification string for all of this composite's children.
216     *
217     * @param string    $ident      The new identification string.
218     *
219     * @access public
220     * @since  Log 1.6.7
221     */
222    function setIdent($ident)
223    {
224        /* Call our base class's setIdent() method. */
225        parent::setIdent($ident);
226
227        /* ... and then call setIdent() on all of our children. */
228        foreach ($this->_children as $child) {
229            $child->setIdent($ident);
230        }
231    }
232
233    /**
234     * Adds a Log instance to the list of children.
235     *
236     * @param object    $child      The Log instance to add.
237     *
238     * @return boolean  True if the Log instance was successfully added.
239     *
240     * @access public
241     */
242    function addChild(&$child)
243    {
244        /* Make sure this is a Log instance. */
245        if (!is_a($child, 'Log')) {
246            return false;
247        }
248
249        $this->_children[$child->_id] = $child;
250
251        return true;
252    }
253
254    /**
255     * Removes a Log instance from the list of children.
256     *
257     * @param object    $child      The Log instance to remove.
258     *
259     * @return boolean  True if the Log instance was successfully removed.
260     *
261     * @access public
262     */
263    function removeChild($child)
264    {
265        if (!is_a($child, 'Log') || !isset($this->_children[$child->_id])) {
266            return false;
267        }
268
269        unset($this->_children[$child->_id]);
270
271        return true;
272    }
273
274}
275