3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2004-2005 Juan Luis Frances Jiminez |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU General Public License |
8 | as published by the Free Software Foundation; either version 2 |
9 | of the License, or (at your option) any later version. |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 +-------------------------------------------------------------------------+
18 define('CONFIG_DIR', "configs");
19 define('CONFIG_FILE', "bacula.conf");
20 define('BACULA_TYPE_BYTES_FILES', 1);
21 define('BACULA_TYPE_FILES_JOBID', 2);
22 define('BACULA_TYPE_BYTES_ENDTIME_ALLJOBS', 69);
24 require_once "paths.php";
25 require_once "DB.php"; // Pear DB
26 require_once "config.inc.php";
27 require_once($smarty_path."Config_File.class.php");
29 if (!function_exists('array_fill')) { // For PHP < 4.2.0 users
30 require_once('array_fill.func.php');
33 class Bweb extends DB {
41 public $db_link; // Database link
42 private $db_dsn; // Data Source Name
44 private $config_file; // Config filename
45 private $config; // Loaded config from bacula.conf
46 private $catalogs = array(); // Catalog array
48 function __construct()
50 $this->catalogs = array();
52 // Loading configuration
53 $this->config_file = getcwd() . '/configs/bacula.conf';
54 if( !$this->load_config() )
55 die( "Unable to load configuration");
57 //echo "Number of catalog defined " . count($this->catalogs) . "<br />";
60 $conf = new Config_File (CONFIG_DIR);
64 $sections = $conf->get(CONFIG_FILE,"DATABASE","host");
65 array_push($this->dbs, "DATABASE");
67 while ( !empty($sections) ) {
68 $sections = $conf->get(CONFIG_FILE,"DATABASE".$i,"host");
69 if ( !empty($sections) )
70 array_push($this->dbs,"DATABASE".$i);
77 if ( !empty($_POST['sel_database']) ) {
78 $_SESSION['DATABASE'] = $_POST['sel_database'];
79 $sec = $_POST['sel_database'];
81 if (isset($_SESSION['DATABASE']) )
82 $sec = $_SESSION['DATABASE'];
88 $this->dsn['hostspec'] = $conf->get(CONFIG_FILE,$sec,"host");
89 $this->dsn['username'] = $conf->get(CONFIG_FILE,$sec,"login");
90 $this->dsn['password'] = $conf->get(CONFIG_FILE,$sec,"pass");
91 $this->dsn['database'] = $conf->get(CONFIG_FILE,$sec,"db_name");
92 $this->dsn['phptype'] = $conf->get(CONFIG_FILE,$sec,"db_type"); // mysql, pgsql
94 if ( $conf->get(CONFIG_FILE,$sec,"db_port") )
95 $this->dsn[port] = $conf->get(CONFIG_FILE,$sec,"db_port");
98 // Construct a valid dsn
99 $this->db_dsn['hostspec'] = $this->catalogs[0]["host"];
100 $this->db_dsn['username'] = $this->catalogs[0]["login"];
101 $this->db_dsn['password'] = $this->catalogs[0]["pass"];
102 $this->db_dsn['database'] = $this->catalogs[0]["db_name"];
103 $this->db_dsn['phptype'] = $this->catalogs[0]["db_type"];
106 $this->db_link = $this->connect($this->db_dsn);
108 if (DB::isError($this->db_link)) {
109 die($this->db_link->getMessage());
111 $this->driver = $this->db_dsn['phptype'];
112 register_shutdown_function(array(&$this,'close'));
113 $this->dbs_name = $this->db_dsn['database'];
117 function load_config()
119 $this->config = parse_ini_file( $this->config_file, true );
121 if( !$this->config == false ) {
122 // Loading database connection information
123 foreach( $this->config as $parameter => $value )
125 //echo "Param $parameter = $value <br />";
126 if( is_array($value) ){ // Parsing database section
127 array_push( $this->catalogs, $value );
135 public function get_config_param( $param )
137 if( isset( $this->config[$param] ) )
138 return $this->config[$param];
143 public function Get_Nb_Catalogs()
145 return count( $this->catalogs );
151 $this->db_link->disconnect();
156 function CalculateBytesPeriod($server,$StartDate,$EndPeriod) { // Bytes transferred in a period.
158 $result =& $this->db_link->query("select SUM(JobBytes) from Job WHERE EndTime < '$EndPeriod' and EndTime > '$StartDate' and Name='$server'")
159 or die("classes.inc: Error query: 1");
160 $return =& $result->fetchRow();
166 function CalculateFilesPeriod($server,$StartDate,$EndPeriod) { // Number of files transferred in a period.
168 $result =& $this->db_link->query("select SUM(JobFiles) from Job WHERE EndTime < '$EndPeriod' and EndTime > '$StartDate' and Name='$server'")
169 or die("classes.inc: Error query: 2");
170 $return =& $result->fetchRow();
176 function PrepareDate($StartDateMonth,$StartDateDay,$StartDateYear,$EndDateMonth,$EndDateDay,$EndDateYear) { // Convert date for Smarty. Check if only works with Mysql.
178 $this->StartDate=$StartDateYear."-".$StartDateMonth."-".$StartDateDay." 00:00:00";
179 $this->EndDate=$EndDateYear."-".$EndDateMonth."-".$EndDateDay." 23:59:59"; // last day full
183 function GetDataVolumes() {
186 $res = $this->db_link->query("SELECT Name FROM Pool");
187 while ( $tmp =& $res->fetchRow() ) {
188 if ($this->driver == "mysql" )
189 $result = $this->db_link->query("select Media.VolumeName, Media.VolBytes,Media.VolStatus,Pool.Name,Media.MediaType,Media.LastWritten,FROM_UNIXTIME(UNIX_TIMESTAMP(Media.LastWritten)+Media.VolRetention ) as expire from Pool LEFT JOIN Media ON Media.PoolId=Pool.PoolId where Name='$tmp[0]' order by Media.VolumeName");
190 else if ($this->driver == "pgsql")
191 $result = $this->db_link->db_query("select Media.VolumeName, Media.VolBytes,Media.VolStatus,Pool.Name,Media.MediaType,Media.LastWritten, Media.LastWritten + Media.VolRetention * interval '1 second' as expire from Pool LEFT JOIN Media ON Media.PoolId=Pool.PoolId where Name='$tmp[0]' order by Media.VolumeName");
192 while ( $tmp1 = $result->fetchRow() ) {
193 $pos = array_key_exists($tmp[0],$volume);
195 array_push($volume["$tmp[0]"],$tmp1);
197 $volume += array($tmp[0]=>array($tmp1));
206 function human_file_size( $size, $decimal = 2 )
210 $units = array('B','KB','MB','GB','TB');
214 if ( $hsize >= 1024 ) {
215 $hsize = $hsize / 1024;
223 $hsize = sprintf("%." . $decimal . "f", $hsize);
224 return $hsize . ' ' . $units[$unit_id];
231 if ( $this->driver == "mysql") {
232 $dbsize = $this->db_link->query("show table status") or die ("classes.inc: Error query: 3");
234 if ( $dbsize->numRows() ) {
235 while ( $res = $dbsize->fetchRow(DB_FETCHMODE_ASSOC) )
236 $database_size += $res["Data_length"];
241 else if ( $this->driver == "pgsql") {
242 $dbsize = $this->db_link->query("select pg_database_size('$this->dbs_name')") or die ("classes.inc: Error query: 4");
244 if (PEAR::isError($dbsize))
245 die($dbsize->getMessage());
247 if ( $dbsize->numRows() ) {
248 while ( $res = $dbsize->fetchRow() )
249 $database_size += $res[0];
257 return $this->human_file_size( $database_size );
258 } // end function GetDbSize()
260 public function Get_Nb_Clients()
262 $clients = $this->db_link->query("SELECT COUNT(*) AS nb_client FROM Client");
263 if( PEAR::isError($clients) )
264 die( "Unable to get client number" );
266 return $clients->fetchRow( DB_FETCHMODE_ASSOC );
269 // Return an array of volumes ordered by poolid and volume name
270 function GetVolumeList() {
276 // Get the list of pools id
277 $query = "SELECT Pool.poolid, Pool.name FROM Pool ORDER BY Pool.poolid";
279 $this->db_link->setFetchMode(DB_FETCHMODE_ASSOC);
280 $pools = $this->db_link->query( $query );
282 if( PEAR::isError( $pools ) )
283 die("Error: Failed to get pool list <br />SQL Query: $query<br />" . $pools->getMessage() );
285 while( $pool = $pools->fetchRow() ) {
286 switch( $this->driver )
290 $query = "SELECT Media.VolumeName, Media.VolBytes, Media.VolStatus, Pool.Name, Media.MediaType,Media.LastWritten, FROM_UNIXTIME(UNIX_TIMESTAMP(Media.LastWritten)+Media.VolRetention ) AS expire
291 FROM Pool LEFT JOIN Media ON Media.PoolId=Pool.PoolId WHERE poolid='$pool[0]'
292 ORDER BY Media.VolumeName";
294 $query = "SELECT Media.volumename, Media.volbytes, Media.volstatus, Media.mediatype, Media.lastwritten, Media.volretention
295 FROM Media LEFT JOIN Pool ON Media.poolid = Pool.poolid
296 WHERE Media.poolid = '". $pool['poolid'] . "' ORDER BY Media.volumename";
299 $query = "SELECT media.volumename, media.volbytes, media.volstatus, media.mediatype, media.lastwritten, media.volretention
300 FROM media LEFT JOIN pool ON media.poolid = pool.poolid
301 WHERE media.poolid = '". $pool['poolid'] . "' ORDER BY media.volumename";
303 $query = "SELECT Media.VolumeName, Media.VolBytes,Media.VolStatus,Pool.Name,Media.MediaType,Media.LastWritten, Media.LastWritten + Media.VolRetention * interval '1 second' AS expire
304 FROM Pool LEFT JOIN Media ON media.poolid=pool.poolid WHERE poolid='$pool[0]'
305 ORDER BY Media.VolumeName";
309 $query = ""; // not yet implemented
315 $this->db_link->setFetchMode(DB_FETCHMODE_ASSOC);
316 $medias = $this->db_link->query( $query );
318 if( PEAR::isError( $medias ) ) {
319 die( "Failed to get media list for pool $volume[0] <br /> " . $medias->getMessage() );
321 if( $debug ) echo "Found " . $medias->numRows() . " medias for pool " . $pool['name'] . " <br />";
323 // Create array key for each pool
324 if( !array_key_exists( $pool['name'], $volumes) )
326 $volumes[ $pool['name'] ] = array();
328 while( $media = $medias->fetchRow() ) {
333 if( $medias->numRows() == 0 ) {
334 if( $debug ) echo "No media in pool " . $pool['name'] . "<br />";
336 if( ($media['lastwritten'] != "0000-00-00 00:00:00") && $media['volstatus'] == 'Full' ) {
337 // Calculate expiration date
338 $expire_date = strtotime($media['lastwritten']) + $media['volretention'];
339 $media['expire'] = strftime("%Y-%m-%d", $expire_date);
341 // Media used size in a more readable format
342 $media['volbytes'] = $this->human_file_size( $media['volbytes'] );
344 $media['lastwritten'] = "N/A";
345 $media['expire'] = "N/A";
346 $media['volbytes'] = "0 KB";
348 // Add the media in pool array
349 array_push( $volumes[ $pool['name']], $media);
355 } // end function GetVolumeList()
357 public function GetLastJobs( $delay = LAST_DAY )
363 // Interval calculation
364 $end_date = mktime();
365 $start_date = $end_date - $delay;
367 $start_date = date( "Y-m-d H:m:s", $start_date );
368 $end_date = date( "Y-m-d H:m:s", $end_date );
370 switch( $this->driver )
373 $query = 'SELECT COUNT(JobId) AS completed_jobs ';
374 $query .= 'FROM Job ';
375 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date' ";
376 $query .= "AND JobStatus = 'T'";
380 $jobs = $this->db_link->query( $query );
382 if (PEAR::isError( $jobs ) ) {
383 die( "Unable to get last completed jobs status from catalog<br />" . $status->getMessage() );
385 return $jobs->fetchRow();
387 } // end function GetLastJobStatus()
389 public function GetLastErrorJobs( $delay = LAST_DAY )
395 // Interval calculation
396 $end_date = mktime();
397 $start_date = $end_date - $delay;
399 $start_date = date( "Y-m-d H:m:s", $start_date );
400 $end_date = date( "Y-m-d H:m:s", $end_date );
402 //echo "start date: $start_date <br />";
403 //echo "end date: $end_date <br />";
405 switch( $this->driver )
408 $query = 'SELECT COUNT(JobId) AS failed_jobs ';
409 $query .= 'FROM Job ';
410 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date' ";
411 $query .= "AND JobStatus = 'f'";
414 $result = $this->db_link->query( $query );
416 if (PEAR::isError( $result ) ) {
417 die( "Unable to get last failed jobs status from catalog<br />query = $query <br />" . $result->getMessage() );
419 return $result->fetchRow( DB_FETCHMODE_ASSOC );
421 } // end function GetLastErrorJobs
423 public function Get_BackupJob_Names()
425 $query = "SELECT Name FROM Job GROUP BY Name";
426 $backupjobs = array();
428 $result = $this->db_link->query( $query );
430 if (PEAR::isError( $result ) ) {
431 die("Unable to get BackupJobs list from catalog" );
433 while( $backupjob = $result->fetchRow( DB_FETCHMODE_ASSOC ) ) {
434 array_push( $backupjobs, $backupjob["Name"] );
440 public function Get_ElapsedTime_Job( $delay = LAST_DAY )
445 // Interval calculation
446 $end_date = mktime();
447 $start_date = $end_date - $delay;
449 $start_date = date( "Y-m-d H:m:s", $start_date );
450 $end_date = date( "Y-m-d H:m:s", $end_date );
452 switch( $this->driver )
455 $query = "SELECT UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime) AS elapsed from Job ";
456 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
459 $result = $this->db_link->query( $query );
461 if( PEAR::isError($result) ){
462 die( "Unable to get elapsed time for jobs from catalog<br />query = $query <br />" . $result->getMessage() );
464 while( $time = $result->fetchRow( DB_FETCHMODE_ASSOC ) ) {
465 //echo 'elapsed = ' . $time['elapsed'] . '<br />';
466 $total_elapsed += $time['elapsed'];
468 // Verify if elapsed time is more than 1 day
469 if ( $total_elapsed > 86400 ) {
470 return gmstrftime("%d days %H:%M:%S", $total_elapsed );
472 return gmstrftime("%H:%M:%S", $total_elapsed );
491 function BShowGraph($datos,$title,$xlabel,$ylabel,$leyenda,$tipo="lines") {
495 require_once ("external_packages/phplot/phplot.php");
497 if ( empty($this->sizex) || empty($this->sizey) ) { //Default size
498 $this->sizex = "600";
499 $this->sizey = "400";
501 if ( empty($this->MarginBottom) ) {
502 $this->MarginBottom = 120;
506 // $bgcolor = array(222,206,215); // Background color of graph
507 $bgcolor = array(207,231,231);
508 $fgcolor = array(110,41,57);
512 $graph = new PHPlot($this->sizex,$this->sizey,"","");
515 $graph->setDataType($type);
517 $graph->SetDataValues($datos);
518 $graph->SetPlotType($tipo);
519 // $graph->SetUseTTF(1);
520 $graph->SetBackgroundColor($bgcolor);
522 $graph->SetLegendPixels(1,20);
523 $graph->SetDataColors(array('SkyBlue','purple','PeachPuff','aquamarine1','#2CB04B','beige','#9F865F','#135568','orchid','navy','red', 'black', 'blue', 'green', 'brown', 'yellow','cyan','orange','#B9F5A7','#AFAFAF'));
524 $graph->SetTitle($title);
525 $graph->SetXLabel($xlabel);
526 $graph->SetYLabel($ylabel);
527 $graph->SetPlotAreaWorld("","","","");
529 if ( count($datos) > 5 )
530 $graph->SetXDataLabelAngle(90);
532 $graph->SetXDataLabelAngle(0);
533 $graph->SetNumXTicks(1);
534 // $graph->SetXDataLabelPos('none');
535 // $graph->SetXTickLabelPos('plotdown');
537 // $graph->SetXGridLabelType("time");
538 // $graph->SetXTimeFormat("%b ") ;
540 if ( $this->Leg == 1 ) {
541 $this->MarginLeftWithLegend($legend);
542 $graph->SetMarginsPixels($this->MarginLeft,10,35,$this->MarginBottom);
543 $graph->SetLegend($legend);
546 $graph->SetMarginsPixels(90,35,35,$this->MarginBottom);
547 // $graph->SetDataColors(array($fgcolor),array( "black"));
548 $graph->SetFileFormat( "png");
549 // $graph->DoScaleData(1,1);
550 // $graph->DoMovingAverage(1,1,1);
552 // FIX ME -- to round y axis.
553 $vtick = strlen (round ($graph->max_y));
555 for ($i=1;$i < $vtick; $i++)
557 if (strlen($graph->max_y-$res) != $vtick )
559 $graph->SetVertTickIncrement($res);
565 //Estupidez que tengo que cambiar. !!!!!!!!!!!
566 function SetDataType($typ) {
572 function MarginLeftWithLegend($clients) {
576 while (next($clients)) {
577 $tmp = strlen(current($clients));
578 if ( $tmp > $maxlen )
581 $this->MarginLeft = $maxlen * 9;
590 class BCreateGraph extends BGraph {
597 var $elapsed; // Default elapsed time to show complex graphs
601 function BCreateGraph() {
603 $this->StartDate = "1900-01-01";
604 $this->EndDate = "4000-01-01";
605 $this->elapsed = "86400"; // 24 hours in seconds.
611 function BCreate($server,$tipo_dato,$title,$tipo="bars",$xlabel="",$ylabel="") {
618 $this->clientes=array();
619 $DB_bacula = new Bweb();
620 $datos = $this->SQLPrepareData($server,$tipo_dato);
622 if ( empty($datos) ) { //No data = No stats = Empty graph
623 header("Content-type: image/png");
624 $img= @ImageCreate(200,100) or die ("Cannot intialize GD stream");
625 $bgc= ImageColorAllocate($img, 0, 255,255);
626 $txc= ImageColorAllocate($img, 0,0,0);
627 ImageString($img, 5, 4, 4, "No data to process", $txc);
633 if ( empty ($xlabel) ) { // If no label, table names like leyends
634 $xlabel=$derecha; $ylabel=$izquierda;
637 $this->SetDataType("text-data");
638 $this->BShowGraph($datos,$title,$xlabel,$ylabel,$this->clientes,$tipo);
644 function SQLPrepareData($servidor,$tipo_dato=0) { // Prepare bytes data from database.
650 if ( $tipo_dato<30 ) { // Simple graph. Only 2 data
654 case BACULA_TYPE_BYTES_FILES:
655 $izquierda="jobbytes";
658 case BACULA_TYPE_FILES_JOBID:
659 $izquierda="jobfiles";
663 $izquierda="jobbytes";
667 $result = $DB_bacula->db_link->query("select $derecha,$izquierda from Job where Name='$servidor' and EndTime < '$this->EndDate' and EndTime > '$this->StartDate' order by SchedTime asc")
668 or die ("classes.inc: Error at query: 5");
669 while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
670 $whole_result[] = $this->array_merge_php4($row["$derecha"],$row[$izquierda]);
673 } else { // Complex graph. 3 or more data.
675 switch ( $tipo_dato )
677 case '30': // Unused, at this time.
678 $result = $DB_bacula->db_link->query("select JobBytes,JobFiles,Jobid from Job where Name='$servidor' order by EndTime asc")
679 or die ("classes.inc: Error at query: 6");
680 while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) )
681 $whole_result[] = array_merge($row["Jobid"],$row["JobFiles"],$row["JobBytes"]);
684 case BACULA_TYPE_BYTES_ENDTIME_ALLJOBS: // Special: Generic graph from all clientes.
685 $i = -1; // Counter of number of jobs of one client. SP: Contador del nmero de jobs totales de un cliente.
686 $i2 = 0; // Counter of number of keys of array. SP: Contador del nmero de valores del array.
688 if ($DB_bacula->driver == "mysql") {
689 $res = $DB_bacula->db_link->query("select Name from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed group by Name order by Name desc")
690 or die ("classes.inc: Error at query: 7");
691 $resdata = $DB_bacula->db_link->query("select date_format(EndTime,\"%Y-%m-%d\") from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed group by date_format(EndTime, \"%Y-%m-%d\") order by EndTime")
692 or die ("classes.inc: Error at query: 8");
694 else if ($DB_bacula->driver == "pgsql") {
695 $res = $DB_bacula->db_link->query("select Name from Job where EndTime > now() - 1*interval'$this->elapsed s' group by Name order by Name desc")
696 or die ("classes.inc: Error at query: 8");
697 $resdata = $DB_bacula->db_link->query("select to_char(EndTime,'YY-MM-DD') from Job where EndTime > NOW() - 1*interval'$this->elapsed s' group by EndTime order by EndTime")
698 or die ("classes.inc: Error at query: 9");
701 if (PEAR::isError($resdata))
702 die("classes.inc: Error at query: 9.1<br>".$resdata->getMessage());
703 while ( $tmpdata = $res->fetchRow() )
704 array_push($this->clientes,$tmpdata[0]);
707 // print_r ($this->clientes);
711 $spr = array(); // Temporal array
712 $spr2 = array(); // Temporal array
713 $whole_result = array();
716 while ( $tmpdata = $resdata->fetchRow() ) {
718 array_push($spr,$tmpdata[0]);
719 if ($DB_bacula->driver == "mysql")
720 $result = $DB_bacula->db_link->query("select date_format(EndTime,\"%Y-%m-%d\"),SUM(JobBytes) as sum,Name as name,count(Name) as Nname from Job WHERE EndTime like '$tmpdata[0]%' group by Name order by Name desc")
721 or die ("classes.inc: Error at query: 10");
722 else if ($DB_bacula->driver == "pgsql") {
723 $query = "select to_char(EndTime,'YY-MM-DD'),SUM(JobBytes) as sum,Name,count(Name) as Nname from Job WHERE EndTime like '%$tmpdata[0]%' group by EndTime,Name order by Name desc";
724 $result = $DB_bacula->db_link->query($query)
725 or die ("classes.inc: Error at query: 11");
727 while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
728 $spr2 = array_merge($spr2,array($row["name"]=>$row["sum"]));
729 $i = $result->numRows();
737 reset ($this->clientes);
739 if ( $spr2[current($this->clientes)] != NULL)
740 array_push($spr,$spr2[current($this->clientes)]);
743 } while ( next($this->clientes) );
748 if ( $tmpdata[0] != $row["EndTime"] )
749 array_push($whole_result,$spr);
755 for ( $i = 0; $i < count($whole_result); $i++ ) { // To equal the arrays so that the graph is not unsquared. SP:Igualamos las matrices para que la gr�ica no se descuadre
756 $tmp = count($whole_result[$i]);
757 if ( $i2 < $tmp ) // Estupidez?. Check this code later...
760 for ( $a = 0; $a <= $tmp; $a++ )
761 array_push($whole_result[$i],"0"); // Fill the array
764 // echo "DEBUG:<br>";
766 // print_r ($whole_result);
775 return $whole_result;
780 //Convert date from mysql to smarty. THE SAME FUNCTION AT 2 CLASSES. THIS WAY IS BUGGY. TO SOLVE LATER.
781 function PrepareDate($StartDateMonth,$StartDateDay,$StartDateYear,$EndDateMonth,$EndDateDay,$EndDateYear){
783 $this->StartDate = $StartDateYear."-".$StartDateMonth."-".$StartDateDay." 00:00:00";
784 $this->EndDate = $EndDateYear."-".$EndDateMonth."-".$EndDateDay." 23:59:00";
789 function array_merge_php4($array1,$array2) {
792 foreach(func_get_args() as $arg) {
796 foreach($arg as $key=>$val){