Documentation is available at tgcSession.php
1 <?php
2 error_reporting(E_ALL);
3 /**
4 * $Id: tgcSession.php,v 1.2 2004/04/28 21:13:12 luckec Exp $
5 *
6 * tgcSession is a wrapper-class for PHP's session-functions
7 *
8 * @package tgcSession
9 * @author Carsten Lucke <luckec@tool-garage.de>
10 * @copyright Carsten Lucke <http://www.tool-garage.de>
11 */
12
13
14 /**
15 * tgcSession is a wrapper-class for PHP's session-functions
16 *
17 * depending on the used version of PHP it uses a certain way of registering
18 * and unregistering session variables and so on
19 *
20 * @package tgcSession
21 * @access public
22 * @version 1.1.1
23 * @author Carsten Lucke <luckec@tool-garage.de>
24 */
25 class tgcSession
26 {
27 /**
28 * name of the session
29 *
30 * @var string name of the session
31 * @access private
32 ***/
33 var $_sessionName;
34
35
36 /**
37 * session-id
38 *
39 * @var string session-id
40 * @access private
41 ***/
42 var $_sessionId;
43
44
45 /**
46 * php-version
47 *
48 * @var string php-version
49 * @access private
50 ***/
51 var $_phpVersion;
52
53
54 /**
55 * register_globals
56 *
57 * @var boolean register_globals enabled or not
58 * @access private
59 ***/
60 var $_registerGlobalsEnabled;
61
62
63 /**
64 * custom variable used for version management
65 *
66 * as version_compare() is only available since PHP 4.1.0. this variable
67 * is used to decide whether superglobals can be used or not
68 *
69 * if true, Superglobals are available - else they are not
70 *
71 * @var boolean superglobals are available
72 * @access private
73 * @see tgcSession()
74 */
75 var $_useSuperglobals;
76
77
78
79 /**
80 * Constructor
81 *
82 * the constructor has to be called before any headers are sent,
83 * it starts a new session or continues an existing one
84 *
85 * the param $settings indicates what has to be done before starting the session,
86 * it may contain the following items, each with a value to set:
87 *
88 * <pre>
89 * "setSavePath" - set the path of the directory used to save session data
90 * "setSessionName" - set the session-name
91 * "setSessionId" - set the session-id
92 * "setCacheExpire" - set the cache-expire time (since PHP 4.2.0)
93 * "setCacheLimiter" - set the name of the current cache limiter
94 * </pre>
95 *
96 * @access public
97 * @param array $settings settings to do before starting the session
98 * @see getSavePath(), getSessionName(), getSessionId(), getCacheExpire(), getCacheLimiter()
99 */
100 function tgcSession($settings = array())
101 {
102 $this->_phpVersion = phpversion();
103 $this->_useSuperglobals = function_exists('version_compare');
104 $this->_registerGlobalsEnabled = (ini_get('register_globals')==1) ? true : false;
105
106 if (is_array($settings)) {
107 foreach ($settings as $key => $value) {
108 switch ($key) {
109 case 'setSavePath':
110 $this->_setSavePath($value);
111 break;
112 case 'setSessionName':
113 $this->_setSessionName($value);
114 break;
115 case 'setSessionId':
116 $this->_setSessionId($value);
117 break;
118 case 'setCacheExpire':
119 $this->_setCacheExpire($value);
120 break;
121 case 'setCacheLimiter':
122 $this->_setCacheLimiter($value);
123 break;
124 }
125 }
126 }
127
128 session_start();
129 $this->_sessionId = session_id();
130 $this->_sessionName = session_name();
131 }
132
133
134 /**
135 * @access public
136 * @param string $varName name of the session variable
137 * @param mixed $varValue value to be stored in the session under this certain name
138 * @deprecated Don't use this method anymore as it will disappear in the next release. Use setVar() instead.
139 * @see setVar()
140 */
141 function set_var($varName, $varValue)
142 {
143 $this->setVar($varName, $varValue);
144 }
145
146
147 /**
148 * add a variable/object to the session
149 *
150 * use this method to add a new variable or object to the current session
151 *
152 * @access public
153 * @param string $varName name of the session variable
154 * @param mixed $varValue value to be stored in the session under this certain name
155 * @see setVar()
156 */
157 function setVar($varName, $varValue)
158 {
159 if ($this->_registerGlobalsEnabled==true) {
160 if ($this->_useSuperglobals == true) {
161 $_SESSION[$varName] = $varValue;
162 $GLOBALS[$varName] = $varValue;
163 } else {
164 $GLOBALS['HTTP_SESSION_VARS'][$varName] = $varValue;
165 session_register($varName);
166 $GLOBALS[$varName] = $varValue;
167 }
168 } else {
169 if ($this->_useSuperglobals == true) {
170 $_SESSION[$varName] = $varValue;
171 } else {
172 $GLOBALS['HTTP_SESSION_VARS'][$varName] = $varValue;
173 }
174 }
175 }
176
177
178 /**
179 * @access public
180 * @param string $varName name of the session variable
181 * @return mixed value stored under the given name
182 * @deprecated Don't use this method anymore as it will disappear in the next release. Use getVar() instead.
183 * @see getVar()
184 */
185 function get_var($varName)
186 {
187 $this->getVar($varName);
188 }
189
190
191 /**
192 * returns a session variable
193 *
194 * this method returns the value stored in the current session under the given name
195 *
196 * @access public
197 * @param string $varName name of the session variable
198 * @return mixed value stored under the given name
199 * @see setVar()
200 */
201 function getVar($varName)
202 {
203 if ($this->_useSuperglobals == true) {
204 if (isset($_SESSION[$varName])) {
205 return $_SESSION[$varName];
206 }
207 return null;
208 } else {
209 if (isset($GLOBALS['HTTP_SESSION_VARS'][$varName])) {
210 return $GLOBALS['HTTP_SESSION_VARS'][$varName];
211 }
212 return null;
213 }
214 }
215
216
217 /**
218 * @access public
219 * @return string string consisting of session-name and session-id
220 * @deprecated Don't use this method anymore as it will disappear in the next release. Use getUrlString() instead.
221 * @see getUrlString()
222 */
223 function get_sid_string()
224 {
225 return $this->getUrlString();
226 }
227
228
229 /**
230 * returns a string to append on URLs
231 *
232 * this method returns a string like PHPSESSID=f57168af293ea522be176cc13abd5aa3 that
233 * can be appended to an URL if necessary
234 *
235 * @access public
236 * @return string String consisting of session-name and session-id
237 */
238 function getUrlString()
239 {
240 return $this->_sessionName . '=' . $this->_sessionId;
241 }
242
243
244 /**
245 * @access public
246 * @return string session-id
247 * @deprecated Don't use this method anymore as it will disappear in the next release. Use getSessionId() instead.
248 * @see getVar()
249 */
250 function get_sid()
251 {
252 return $this->getSessionId();
253 }
254
255
256 /**
257 * returns the session-id
258 *
259 * returns the current session-id
260 *
261 * @access public
262 * @return string session-id
263 * @see tgcSession()
264 */
265 function getSessionId()
266 {
267 return $this->_sessionId;
268 }
269
270
271 /**
272 * sets the session-id
273 *
274 * sets the current session-id
275 *
276 * as this method does nothing else than using PHP's session_id() please
277 * see http://www.php.net/manual/en/function.session-id.php for more information
278 *
279 * @access private
280 * @param string new session-id
281 * @return string session-id
282 */
283 function _setSessionId($newSessionId)
284 {
285 $this->_sessionId = $newSessionId;
286 return session_id($newSessionId);
287 }
288
289
290 /**
291 * @access public
292 * @param string $varName name of the variable
293 * @deprecated Don't use this method anymore as it will disappear in the next release. Use unsetVar() instead.
294 * @see unsetVar()
295 */
296 function var_unset( $varName )
297 {
298 $this->unsetVar($varName);
299 }
300
301
302 /**
303 * unsets a session variable
304 *
305 * use this method to remove a certain variable from the session
306 *
307 * @access public
308 * @param string $varName name of the variable
309 * @see unsetAll(), destroy()
310 */
311 function unsetVar($varName)
312 {
313 if ($this->_registerGlobalsEnabled==true) {
314 if ($this->_useSuperglobals == true) {
315 unset($_SESSION[$varName]);
316 unset($GLOBALS[$varName]);
317 } else {
318 unset($GLOBALS['HTTP_SESSION_VARS'][$varName]);
319 session_unregister($varName);
320 }
321 } else {
322 if ($this->_useSuperglobals == true) {
323 unset($_SESSION[$varName]);
324 } else {
325 unset($GLOBALS['HTTP_SESSION_VARS'][$varName]);
326 }
327 }
328 }
329
330
331 /**
332 * @access public
333 * @deprecated Don't use this method anymore as it will disappear in the next release. Use unsetAll() instead.
334 * @see unsetAll()
335 */
336 function ses_unset()
337 {
338 $this->unsetAll();
339 }
340
341
342 /**
343 * unsets all session variables
344 *
345 * use this method to unset every variable you stored in the session
346 *
347 * @access public
348 * @see unsetVar(), destroy()
349 */
350 function unsetAll()
351 {
352 if ($this->_useSuperglobals == true) {
353 foreach (array_keys($_SESSION) as $key) {
354 $this->unsetVar($key);
355 }
356 } else {
357 global $HTTP_SESSION_VARS
358 foreach (array_keys($GLOBALS['HTTP_SESSION_VARS']) as $key) {
359 $this->unsetVar($key);
360 }
361 }
362 }
363
364
365 /**
366 * deletes the session and its data
367 *
368 * deletes the session and its data
369 *
370 * if the optional parameter is set to true, all global variables
371 * that belong to the session are deleted, too
372 *
373 * @access public
374 * @param boolean $deleteGlobals delete globals that belong to the session
375 * @return boolean returns true on success and false on failure to destroy the session data
376 * @see unsetVar(), unsetAll()
377 */
378 function destroy($deleteGlobals = false)
379 {
380 if ($deleteGlobals == true) {
381 $this->unsetAll();
382 }
383 return session_destroy();
384 }
385
386
387 /**
388 * displays the variables currently stored in the session
389 *
390 * displays the variables currently stored in the session
391 * and some information about the object
392 *
393 * @access public
394 */
395 function dump()
396 {
397 $output = '<table cellspacing="0" cellpadding="2" border="1">'; $output .= "\n";
398 $output .= '<tr><th colspan="2">tgcSession dump</th></tr>'; $output .= "\n";
399 $output .= '<tr><td colspan="2"> </td></tr>'; $output .= "\n";
400 $output .= '<tr><th align="left">PHP-Version: </th><td>' . $this->_phpVersion . '</td></tr>'; $output .= "\n";
401 $rg = ($this->_registerGlobalsEnabled == true) ? 'On' : 'Off'; $output .= "\n";
402 $output .= '<tr><th align="left">register_globals: </th><td>' . $rg . '</td></tr>'; $output .= "\n";
403 $output .= '<tr><th align="left">Session-ID: </th><td>' . $this->_sessionId . '</td></tr>'; $output .= "\n";
404 $output .= '<tr><th align="left">Session-Name: </th><td>' . $this->_sessionName . '</td></tr>'; $output .= "\n";
405 $output .= '<tr><th align="left">CacheExpire: </th><td>' . $this->getCacheExpire() . ' minutes </td></tr>'; $output .= "\n";
406 $output .= '<tr><th align="left">CacheLimiter: </th><td>' . $this->getCacheLimiter() . '</td></tr>'; $output .= "\n";
407 $output .= '<tr><th align="left">SavePath: </th><td>' . $this->getSavePath() . '</td></tr>'; $output .= "\n";
408 $output .= '<tr><td colspan="2"> </td></tr>'; $output .= "\n";
409 $output .= '<tr><th colspan="2"> variables in this session </th></tr>'; $output .= "\n";
410
411 if ($this->_useSuperglobals == true) {
412 foreach ($_SESSION as $key => $value) {
413 $output .= '<tr><th align="left">' . $key . ': </th><td>' . $value . '</td></tr>'; $output .= "\n";
414 }
415 } else {
416 foreach ($GLOBALS['HTTP_SESSION_VARS'] as $key => $value) {
417 $output .= '<tr><th align="left">' . $key . ': </th><td>' . $value . '</td></tr>'; $output .= "\n";
418 }
419 }
420
421 echo $output;
422 }
423
424
425 /**
426 * returns the cache-expire time
427 *
428 * returns the current cache-expire time on success,
429 * else the return-value is -1 (since PHP >= 4.2.0)
430 *
431 * as this method does nothing else than using PHP's session_cache_expire() please
432 * see http://www.php.net/manual/en/function.session-cache-expire.php for more information
433 *
434 * @access public
435 * @return int cache-expire time
436 * @see getCacheLimiter(), tgcSession()
437 */
438 function getCacheExpire()
439 {
440 if ($this->_useSuperglobals == true) {
441 if (version_compare($this->_phpVersion,'4.2.0','ge') == true) {
442 return session_cache_expire();
443 }
444 } else {
445 return -1;
446 }
447 }
448
449
450 /**
451 * sets the cache-expire time
452 *
453 * sets the cache-expire time to $cacheExpireTime (since PHP >= 4.2.0)
454 *
455 * as this method does nothing else than using PHP's session_cache_expire() please
456 * see http://www.php.net/manual/en/function.session-cache-expire.php for more information
457 *
458 * @access private
459 * @param int $cacheExpireTime new cache-expire time
460 * @return int cache-expire time
461 * @see getCacheLimiter(), _setCacheLimiter(), getCacheExpire()
462 */
463 function _setCacheExpire($cacheExpireTime)
464 {
465 if ($this->_useSuperglobals == true) {
466 if (version_compare($this->_phpVersion,'4.2.0','ge') == true) {
467 return session_cache_expire($cacheExpireTime);
468 }
469 } else {
470 return -1;
471 }
472 }
473
474
475 /**
476 * returns the current cache limiter
477 *
478 * returns the name of the current cache limiter (since PHP >= 4.0.3)
479 *
480 * as this method does nothing else than using PHP's session_cache_limiter() please
481 * see http://www.php.net/manual/en/function.session-cache-limiter.php for more information
482 *
483 * @access public
484 * @return string current cache limiter
485 * @see getCacheExpire(), tgcSession()
486 */
487 function getCacheLimiter()
488 {
489 // return empty string if the phpversion is not new enough,
490 // else return the cache_limiter
491 if ($this->_customVersionCompare($this->_phpVersion, '4.0.3') == -1) {
492 return '';
493 } else {
494 return session_cache_limiter();
495 }
496 }
497
498
499 /**
500 * sets the current cache limiter
501 *
502 * the name of the current cache limiter is changed to
503 * the value of $cacheLimiter (since PHP >= 4.0.3)
504 *
505 * as this method does nothing else than using PHP's session_cache_limiter() please
506 * see http://www.php.net/manual/en/function.session-cache-limiter.php for more information
507 *
508 * @access private
509 * @param string $cacheLimiter new cache limiter
510 * @return string current cache limiter
511 * @see getCacheLimiter(), getCacheExpire(), _setCacheExpire()
512 */
513 function _setCacheLimiter($cacheLimiter)
514 {
515 // return empty string if the phpversion is not new enough,
516 // else set and return the cache_limiter
517 if ($this->_customVersionCompare($this->_phpVersion, '4.0.3') == -1) {
518 return '';
519 } else {
520 return session_cache_limiter($cacheLimiter);
521 }
522 }
523
524
525 /**
526 * decodes session data from a string
527 *
528 * decodes the session data in $data and sets the variables stored in the session
529 *
530 * @access public
531 * @param string $data variable containing the session data as string
532 * @return boolean returns true if successful, else false
533 * @see encode()
534 */
535 function decode($data)
536 {
537 return session_decode($data);
538 }
539
540
541 /**
542 * encodes the current session data as a string
543 *
544 * returns a string with the contents of the current session encoded within
545 *
546 * @access public
547 * @return string string with the contents of the current session encoded within
548 * @see decode()
549 */
550 function encode()
551 {
552 return session_encode();
553 }
554
555
556 /**
557 * returns the sessions cookie parameters
558 *
559 * returns an array with the current session cookie information,
560 *
561 * the array contains the following items:
562 *
563 * "lifetime" - the lifetime of the cookie
564 *
565 * "path" - the path where information is stored
566
567 * "domain" - the domain of the cookie
568 *
569 * "secure" - the cookie should only be sent over secure connections. (This item was added in PHP 4.0.4.)
570 *
571 *
572 * as this method does nothing else than using PHP's session_get_cookie_params() please
573 * see http://www.php.net/manual/en/function.session-get-cookie-params.php for more information
574 *
575 * @access public
576 * @return array array with the current session cookie information
577 * @see setCookieParams()
578 */
579 function getCookieParams()
580 {
581 return session_get_cookie_params();
582 }
583
584
585 /**
586 * sets the sessions cookie parameters
587 *
588 * sets cookie parameters defined in the php.ini file ,
589 * the effect of this method only lasts for the duration of the script
590 *
591 * as this method does nothing else than using PHP's session_set_cookie_params() please
592 * see http://www.php.net/manual/en/function.session-set-cookie-params.php for more information
593 *
594 * @access public
595 * @param int $lifetime the lifetime of the cookie
596 * @param string $path the path where information is stored
597 * @param string $domain the domain of the cookie
598 * @param boolean $secure the cookie should only be sent over secure connections. (This item was added in PHP 4.0.4.)
599 * @see getCookieParams()
600 */
601 function setCookieParams($lifetime, $path = null, $domain = null, $secure = null)
602 {
603 session_set_cookie_params($lifetime, $path, $domain, $secure);
604 }
605
606
607 /**
608 * checks whether a variable with $varName is registered in the current session
609 *
610 * returns true if there is a variable with the name $varName registered in the current session
611 *
612 * @access public
613 * @param string $varName name of the variable
614 * @return boolean true if such a variable is registered, else false
615 */
616 function isRegistered($varName)
617 {
618 return ($this->getVar($varName)!='') ? true : false;
619 }
620
621
622 /**
623 * returns the session module
624 *
625 * returns the name of the current session module
626 *
627 * @access public
628 * @return string name of the current session module
629 * @see setModuleName()
630 */
631 function getModuleName()
632 {
633 return session_module_name();
634 }
635
636
637 /**
638 * sets the session module
639 *
640 * sets the name of the session module that has to be used
641 *
642 * @access public
643 * @param string $newModuleName name of the new session module
644 * @return string name of the current session module
645 * @see getModuleName()
646 */
647 function setModuleName($newModuleName)
648 {
649 return session_module_name($newModuleName);
650 }
651
652
653 /**
654 * returns the session-name
655 *
656 * returns the name of the current session
657 *
658 * @access public
659 * @return string name of the current session
660 * @see tgcSession()
661 */
662 function getSessionName()
663 {
664 return $this->_sessionName;
665 }
666
667
668 /**
669 * sets the session-name
670 *
671 * sets the name of the current session
672 *
673 * as this method does nothing else than using PHP's session_name() please
674 * see http://www.php.net/manual/en/function.session-name.php for more information
675 *
676 * @access private
677 * @param string $sessionName name for the current session
678 * @return string name of the current session
679 */
680 function _setSessionName($sessionName)
681 {
682 $this->_sessionName = $sessionName;
683 return session_name($sessionName);
684 }
685
686
687 /**
688 * updates the current session id with a newly generated one
689 *
690 * replaces the current session id with a new one,
691 * and keeps the current session information -
692 * available since PHP 4.3.2 or higher
693 *
694 * as this method does nothing else than using PHP's session_regenerate_id() please
695 * see http://www.php.net/manual/en/function.session-regenerate-id.php for more information
696 *
697 *
698 * @access public
699 * @return boolean returns true on success, else false
700 */
701 function regenerateId()
702 {
703 if ($this->_useSuperglobals == true) {
704 if (version_compare($this->_phpVersion,'4.3.2','ge') == true) {
705 return session_regenerate_id();
706 }
707 } else {
708 return false;
709 }
710
711 }
712
713
714 /**
715 * returns the current session save path
716 *
717 * returns the path of the current directory used to save session data
718 *
719 * @access public
720 * @return string path of the current directory used to save session data
721 * @see tgcSession()
722 */
723 function getSavePath()
724 {
725 return session_save_path();
726 }
727
728
729 /**
730 * sets the current session save path
731 *
732 * sets the path of the directory used to save session data
733 *
734 * as this method does nothing else than using PHP's session_save_path() please
735 * see http://www.php.net/manual/en/function.session-save-path.php for more information
736 *
737 * @access private
738 * @param string $savePath sets the path of the directory used to save session data
739 * @return string path of the current directory used to save session data
740 */
741 function _setSavePath($savePath)
742 {
743 return session_save_path($savePath);
744 }
745
746
747 /**
748 * sets user-level session storage functions
749 *
750 * sets the user-level session storage functions which are used for storing and
751 * retrieving data associated with a session
752 *
753 * this is most useful when a storage method other than those supplied by PHP sessions
754 * is preferred. i.e. Storing the session data in a local database
755 *
756 * as this method does nothing else than using PHP's session_set_save_handler() please
757 * see http://www.php.net/manual/en/function.session-set-save-handler.php for more information
758 *
759 * @access public
760 * @param string $open
761 * @param string $close
762 * @param string $read
763 * @param string $write
764 * @param string $destroy
765 * @param string $gc
766 * @return boolean returns true on success or false on failure
767 */
768 function setSaveHandler($open, $close, $read, $write, $destroy, $gc)
769 {
770 return session_set_save_handler($open, $close, $read, $write, $destroy, $gc);
771 }
772
773
774 /**
775 * write session data and end session
776 *
777 * ends the current session and stores session data (since PHP >= 4.0.4)
778 *
779 * as this method does nothing else than using PHP's session_write_close() please
780 * see http://www.php.net/manual/en/function.session-write-close.php for more information
781 *
782 * @access public
783 * @return returns true on success, else false
784 */
785 function writeClose()
786 {
787 if ($this->_customVersionCompare($this->_phpVersion, '4.0.4') == -1) {
788 return false;
789 } else {
790 session_write_close();
791 return true;
792 }
793 }
794
795
796 /**
797 * own version_compare() for PHP-versions < 4.1.0
798 *
799 * own version_compare() to distinguish between two php-versions
800 *
801 * @access private
802 * @param string $v1 version 1
803 * @param string $v2 version 2
804 * @return int -1 if $v1 < $v2, 0 if $v1 == $v2, 1 if $v1 > $v2
805 */
806 function _customVersionCompare($v1, $v2) {
807 // strings to uppercase
808 $v1 = strtoupper($v1);
809 $v2 = strtoupper($v2);
810
811 // strip spaces
812 $v1 = preg_replace('/\ /', '', $v1);
813 $v2 = preg_replace('/\ /', '', $v2);
814
815 // strip trailing point
816 $v1 = preg_replace('/[\.]+$/', '', $v1);
817 $v2 = preg_replace('/[\.]+$/', '', $v2);
818
819 // catch the release candidate if the string is describing one
820 // after that a RC should have the version in $v*Results[1] and
821 // the RC flag in $v*Results[2]
822 $v1IsRC = preg_match('/(^.*)RC(.*$)/', $v1, $v1Results = array()) ? true : false;
823 $v2IsRC = preg_match('/(^.*)RC(.*$)/', $v2, $v2Results = array()) ? true : false;
824
825 // both are RCs
826 if (($v1IsRC == true) && ($v2IsRC == true)) {
827 if ($v1Results[1] < $v2Results[1]) {
828 return -1;
829 } elseif ($v1Results[1] > $v2Results[1]) {
830 return 1;
831 } else {
832 // have to check the RC flag
833 if ($v1Results[2] < $v2Results[2]) {
834 return -1;
835 } elseif ($v1Results[2] == $v2Results[2]) {
836 return 0;
837 } else {
838 return 1;
839 }
840 }
841 } elseif (($v1IsRC == true) && ($v2IsRC == false)) {
842 // $v1 is RC, $v2 is not
843 // compare main versions
844 if ($v1Results[1] < $v2) {
845 return -1;
846 } elseif ($v1Results[1] > $v2) {
847 return 1;
848 } else {
849 // $v1 and $v2 have the same main version but as $v1 is RC it is earlier than $v2
850 return -1;
851 }
852 } elseif (($v1IsRC == false) && ($v2IsRC == true)) {
853 // $v2 is RC, $v1 is not
854 // compare main versions
855 if ($v2Results[1] < $v1) {
856 return 1;
857 } elseif ($v2Results[1] > $v1) {
858 return -1;
859 } else {
860 // $v1 and $v2 have the same main version but as $v2 is RC it is earlier than $v1
861 return 1;
862 }
863 } else {
864 // none is a RC
865 if ($v1 < $v2) {
866 return -1;
867 } elseif ($v1 == $v2) {
868 return 0;
869 } else {
870 return 1;
871 }
872 }
873 }
874 }
875 ?>
Documentation generated on Fri, 19 Nov 2004 23:40:46 +0100 by phpDocumentor 1.2.3