First change to prepare usage of native PHP functions for date instead

of adodb-time library.
This commit is contained in:
Laurent Destailleur 2012-02-05 15:13:31 +01:00
parent 00de718e79
commit 866e8ac556
8 changed files with 299 additions and 480 deletions

View File

@ -155,24 +155,13 @@ print '<tr '.$bc[$var].'><td width="300">&nbsp; => price(1234.56)</td><td>'.pric
// Timezone // Timezone
$txt =$langs->trans("OSTZ").' (variable system TZ): '.($_ENV["TZ"]?$_ENV["TZ"]:$langs->trans("NotDefined")).'<br>'."\n"; $txt =$langs->trans("OSTZ").' (variable system TZ): '.($_ENV["TZ"]?$_ENV["TZ"]:$langs->trans("NotDefined")).'<br>'."\n";
$txt.=$langs->trans("PHPTZ").' (php.ini date.timezone): '.(ini_get("date.timezone")?ini_get("date.timezone"):$langs->trans("NotDefined")).''."\n"; // date.timezone must be in valued defined in http://fr3.php.net/manual/en/timezones.europe.php $txt.=$langs->trans("PHPTZ").' (php.ini date.timezone): '.(ini_get("date.timezone")?ini_get("date.timezone"):$langs->trans("NotDefined")).''."\n"; // date.timezone must be in valued defined in http://fr3.php.net/manual/en/timezones.europe.php
if (function_exists('date_default_timezone_get')) $var=!$var;
{ print '<tr '.$bc[$var].'><td width="300">'.$langs->trans("CurrentTimeZone").'</td><td>'; // Timezone server PHP
$var=!$var; $a=getCurrentTimeZoneString();
print '<tr '.$bc[$var].'><td width="300">'.$langs->trans("CurrentTimeZone").'</td><td>'; // Timezone server PHP $a.=' '.getCurrentTimeZoneInt();
$a=date_default_timezone_get(); $a.=' ('.(-dol_mktime(0,0,0,1,1,1970)>0?'+':'').(-dol_mktime(0,0,0,1,1,1970)).')';
$a.=' '.getCurrentTimeZone(); print $form->textwithtooltip($a,$txt,2,1,img_info(''));
$a.=' ('.(-dol_mktime(0,0,0,1,1,1970)>0?'+':'').(-dol_mktime(0,0,0,1,1,1970)).')'; print '</td></tr>'."\n"; // value defined in http://fr3.php.net/manual/en/timezones.europe.php
print $form->textwithtooltip($a,$txt,2,1,img_info(''));
print '</td></tr>'."\n"; // value defined in http://fr3.php.net/manual/en/timezones.europe.php
}
else
{
$var=!$var;
print '<tr '.$bc[$var].'><td width="300">'.$langs->trans("PHPServerOffsetWithGreenwich").'</td><td>';
$a=(-dol_mktime(0,0,0,1,1,1970)>0?'+':'').(-dol_mktime(0,0,0,1,1,1970));
print $form->textwithtooltip($a,$txt,2,1,img_info(''));
print '</td></tr>'."\n";
}
$var=!$var; $var=!$var;
print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("CurrentHour").'</td><td>'.dol_print_date(dol_now(),'dayhour','tzserver').'</td></tr>'."\n"; print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("CurrentHour").'</td><td>'.dol_print_date(dol_now(),'dayhour','tzserver').'</td></tr>'."\n";
$var=!$var; $var=!$var;
@ -188,18 +177,14 @@ $var=!$var;
print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("CompanyHour").'</td><td>'.$langs->trans("FeatureNotYetAvailable").'</td></tr>'."\n"; print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("CompanyHour").'</td><td>'.$langs->trans("FeatureNotYetAvailable").'</td></tr>'."\n";
// Client // Client
$var=!$var; $var=!$var;
print '<tr '.$bc[$var].'><td width="300">'.$langs->trans("ClientTZ").'</td><td>'.($_SESSION['dol_tz']!=''?($_SESSION['dol_tz']>=0?'+':'').$_SESSION['dol_tz']:'').' ('.($_SESSION['dol_tz']>=0?'+':'').($_SESSION['dol_tz']*60*60).')</td></tr>'."\n"; $tz=(int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst'];
//$var=!$var; print '<tr '.$bc[$var].'><td width="300">'.$langs->trans("ClientTZ").'</td><td>'.($tz?($tz>=0?'+':'').$tz:'').' ('.($tz>=0?'+':'').($tz*60*60).')';
//print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("ClientOffsetWithGreenwich").'</td><td>'..'</td></tr>'."\n"; print ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
$var=!$var; if ($_SESSION['dol_dst']>0) print yn(1);
print '<tr '.$bc[$var].'><td width="300">'.$langs->trans("DaylingSavingTime").'</td><td>';
if ($_SESSION['dol_dst']>0)
{
print ($_SESSION['dol_dst']>0?'+':'').($_SESSION['dol_dst']).' ('.($_SESSION['dol_dst']>0?'+':'').($_SESSION['dol_dst']*60*60).')';
}
else print yn(0); else print yn(0);
if (! empty($_SESSION['dol_dst_first'])) print ' &nbsp; &nbsp; ('.dol_print_date(dol_stringtotime($_SESSION['dol_dst_first']),'dayhour','gmt').' - '.dol_print_date(dol_stringtotime($_SESSION['dol_dst_second']),'dayhour','gmt').')'; if (! empty($_SESSION['dol_dst_first'])) print ' &nbsp; &nbsp; ('.dol_print_date(dol_stringtotime($_SESSION['dol_dst_first']),'dayhour','gmt').' - '.dol_print_date(dol_stringtotime($_SESSION['dol_dst_second']),'dayhour','gmt').')';
print '</td></tr>'."\n"; print '</td></tr>'."\n";
print '</td></tr>'."\n";
$var=!$var; $var=!$var;
print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("ClientHour").'</td><td>'.dol_print_date(dol_now(),'dayhour','tzuser').'</td></tr>'."\n"; print '<tr '.$bc[$var].'><td width="300">&nbsp; => '.$langs->trans("ClientHour").'</td><td>'.dol_print_date(dol_now(),'dayhour','tzuser').'</td></tr>'."\n";

View File

@ -709,7 +709,7 @@ if (count($listofextcals))
$loop=true; $j=0; $loop=true; $j=0;
// daykey must be date that represent day box in calendar so must be a user time // daykey must be date that represent day box in calendar so must be a user time
$daykey=dol_mktime(0,0,0,$mois,$jour,$annee); $daykey=dol_mktime(0,0,0,$mois,$jour,$annee);
$daykeygmt=dol_mktime(0,0,0,$mois,$jour,$annee,true,0,false); $daykeygmt=dol_mktime(0,0,0,$mois,$jour,$annee,true,0);
do do
//print 'x'.$datestart.'-'.$dateend;exit; //print 'x'.$datestart.'-'.$dateend;exit;
{ {

View File

@ -3123,7 +3123,6 @@ class Facture extends CommonObject
$line->desc=$langs->trans("Description")." ".$xnbp; $line->desc=$langs->trans("Description")." ".$xnbp;
$line->qty=1; $line->qty=1;
$line->subprice=100; $line->subprice=100;
//$line->price=100;
$line->tva_tx=19.6; $line->tva_tx=19.6;
$line->localtax1_tx=0; $line->localtax1_tx=0;
$line->localtax2_tx=0; $line->localtax2_tx=0;
@ -3177,7 +3176,6 @@ class Facture extends CommonObject
$line->desc=$langs->trans("Description")." (offered line)"; $line->desc=$langs->trans("Description")." (offered line)";
$line->qty=1; $line->qty=1;
$line->subprice=100; $line->subprice=100;
//$line->price=100;
$line->tva_tx=19.6; $line->tva_tx=19.6;
$line->localtax1_tx=0; $line->localtax1_tx=0;
$line->localtax2_tx=0; $line->localtax2_tx=0;

View File

@ -86,6 +86,7 @@ class Conf
$this->user=(object) array(); $this->user=(object) array();
//! Charset for HTML output and for storing data in memory //! Charset for HTML output and for storing data in memory
$this->file->character_set_client='UTF-8'; // UTF-8, ISO-8859-1 $this->file->character_set_client='UTF-8'; // UTF-8, ISO-8859-1
$this->global->MAIN_OLD_DATE=1;
} }

View File

@ -63,18 +63,39 @@ function get_tz_array()
/** /**
* Return server timezone * Return server timezone string
* *
* @return string TimeZone * @return string PHP server timezone string ('Europe/Paris')
*/ */
function getCurrentTimeZone() function getCurrentTimeZoneString()
{ {
// Method 1 if (function_exists('date_default_timezone_get')) return date_default_timezone_get();
//$tzstring=date_default_timezone_get(); // Then convert into tz else return '';
}
// Method 2
$tmp=dol_mktime(0,0,0,1,1,1970); /**
$tz=($tmp<0?'+':'-').sprintf("%02d",abs($tmp/3600)); * Return server timezone int.
* If $conf->global->MAIN_OLD_DATE is set, we use old behaviour: All convertion does not include daylight.
*
* @return string An offset in seconds (3600 for Europe/Paris on winter and 7200 for Europe/Paris on summer)
*/
function getCurrentTimeZoneInt()
{
global $conf;
if (class_exists('DateTime') && empty($conf->global->MAIN_OLD_DATE))
{
// Method 1 (include daylight)
$localtz = new DateTimeZone(date_default_timezone_get());
$localdt = new DateTime("now", $localtz);
$tmp=-1*$localtz->getOffset($localdt);
}
else
{
// Method 2 (does not include daylight)
$tmp=dol_mktime(0,0,0,1,1,1970);
}
$tz=($tmp<0?'+':'-').abs($tmp/3600);
return $tz; return $tz;
} }

View File

@ -1042,11 +1042,10 @@ function dolibarr_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$chec
* @param int $year Year * @param int $year Year
* @param int $gm 1=Input informations are GMT values, otherwise local to server TZ * @param int $gm 1=Input informations are GMT values, otherwise local to server TZ
* @param int $check 0=No check on parameters (Can use day 32, etc...) * @param int $check 0=No check on parameters (Can use day 32, etc...)
* @param int $isdst Dayling saving time
* @return timestamp Date as a timestamp, '' if error * @return timestamp Date as a timestamp, '' if error
* @see dol_print_date, dol_stringtotime, dol_getdate * @see dol_print_date, dol_stringtotime, dol_getdate
*/ */
function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$check=1,$isdst=true) function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$check=1)
{ {
//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -"; //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";

View File

@ -376,185 +376,6 @@ define('ADODB_DATE_VERSION',0.21);
if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1);
function adodb_date_test_date($y1,$m,$d=13)
{
$t = adodb_mktime(0,0,0,$m,$d,$y1);
$rez = adodb_date('Y-n-j H:i:s',$t);
if ("$y1-$m-$d 00:00:00" != $rez) {
print "<b>$y1 error, expected=$y1-$m-$d 00:00:00, adodb=$rez</b><br>";
return false;
}
return true;
}
function adodb_date_test_strftime($fmt)
{
$s1 = strftime($fmt);
$s2 = adodb_strftime($fmt);
if ($s1 == $s2) return true;
echo "error for $fmt, strftime=$s1, $adodb=$s2<br>";
return false;
}
/**
Test Suite
*/
function adodb_date_test()
{
error_reporting(E_ALL);
print "<h4>Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION.' PHP='.PHP_VERSION."</h4>";
@set_time_limit(0);
$fail = false;
// This flag disables calling of PHP native functions, so we can properly test the code
if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1);
adodb_date_test_strftime('%Y %m %x %X');
adodb_date_test_strftime("%A %d %B %Y");
adodb_date_test_strftime("%H %M S");
$t = adodb_mktime(0,0,0);
if (!(adodb_date('Y-m-d') == date('Y-m-d'))) print 'Error in '.adodb_mktime(0,0,0).'<br>';
$t = adodb_mktime(0,0,0,6,1,2102);
if (!(adodb_date('Y-m-d',$t) == '2102-06-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>';
$t = adodb_mktime(0,0,0,2,1,2102);
if (!(adodb_date('Y-m-d',$t) == '2102-02-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>';
print "<p>Testing gregorian <=> julian conversion<p>";
$t = adodb_mktime(0,0,0,10,11,1492);
//http://www.holidayorigins.com/html/columbus_day.html - Friday check
if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing<br>';
$t = adodb_mktime(0,0,0,2,29,1500);
if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years<br>';
$t = adodb_mktime(0,0,0,2,29,1700);
if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years<br>';
print adodb_mktime(0,0,0,10,4,1582).' ';
print adodb_mktime(0,0,0,10,15,1582);
$diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));
if ($diff != 3600*24) print " <b>Error in gregorian correction = ".($diff/3600/24)." days </b><br>";
print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : '<b>Error</b>')."<br>";
print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : '<b>Error</b>')."<br>";
print "<p>Testing overflow<p>";
$t = adodb_mktime(0,0,0,3,33,1965);
if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1 <br>';
$t = adodb_mktime(0,0,0,4,33,1971);
if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2 <br>';
$t = adodb_mktime(0,0,0,1,60,1965);
if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).' <br>';
$t = adodb_mktime(0,0,0,12,32,1965);
if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).' <br>';
$t = adodb_mktime(0,0,0,12,63,1965);
if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).' <br>';
$t = adodb_mktime(0,0,0,13,3,1965);
if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1 <br>';
print "Testing 2-digit => 4-digit year conversion<p>";
if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000<br>";
if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010<br>";
if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020<br>";
if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030<br>";
if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940<br>";
if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>";
if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>";
// Test string formating
print "<p>Testing date formating</p>";
$fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C822 r s t U w y Y z Z 2003';
$s1 = date($fmt,0);
$s2 = adodb_date($fmt,0);
if ($s1 != $s2) {
print " date() 0 failed<br>$s1<br>$s2<br>";
}
flush();
for ($i=100; --$i > 0; ) {
$ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000);
$s1 = date($fmt,$ts);
$s2 = adodb_date($fmt,$ts);
//print "$s1 <br>$s2 <p>";
$pos = strcmp($s1,$s2);
if (($s1) != ($s2)) {
for ($j=0,$k=strlen($s1); $j < $k; $j++) {
if ($s1[$j] != $s2[$j]) {
print substr($s1,$j).' ';
break;
}
}
print "<b>Error date(): $ts<br><pre>
&nbsp; \"$s1\" (date len=".strlen($s1).")
&nbsp; \"$s2\" (adodb_date len=".strlen($s2).")</b></pre><br>";
$fail = true;
}
$a1 = getdate($ts);
$a2 = adodb_getdate($ts);
$rez = array_diff($a1,$a2);
if (sizeof($rez)>0) {
print "<b>Error getdate() $ts</b><br>";
print_r($a1);
print "<br>";
print_r($a2);
print "<p>";
$fail = true;
}
}
// Test generation of dates outside 1901-2038
print "<p>Testing random dates between 100 and 4000</p>";
adodb_date_test_date(100,1);
for ($i=100; --$i >= 0;) {
$y1 = 100+rand(0,1970-100);
$m = rand(1,12);
adodb_date_test_date($y1,$m);
$y1 = 3000-rand(0,3000-1970);
adodb_date_test_date($y1,$m);
}
print '<p>';
$start = 1960+rand(0,10);
$yrs = 12;
$i = 365.25*86400*($start-1970);
$offset = 36000+rand(10000,60000);
$max = 365*$yrs*86400;
$lastyear = 0;
// we generate a timestamp, convert it to a date, and convert it back to a timestamp
// and check if the roundtrip broke the original timestamp value.
print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: ";
$cnt = 0;
for ($max += $i; $i < $max; $i += $offset) {
$ret = adodb_date('m,d,Y,H,i,s',$i);
$arr = explode(',',$ret);
if ($lastyear != $arr[2]) {
$lastyear = $arr[2];
print " $lastyear ";
flush();
}
$newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]);
if ($i != $newi) {
print "Error at $i, adodb_mktime returned $newi ($ret)";
$fail = true;
break;
}
$cnt += 1;
}
echo "Tested $cnt dates<br>";
if (!$fail) print "<p>Passed !</p>";
else print "<p><b>Failed</b> :-(</p>";
}
/** /**
Returns day of week, 0 = Sunday,... 6=Saturday. Returns day of week, 0 = Sunday,... 6=Saturday.
@ -653,7 +474,7 @@ function adodb_year_digit_check($y)
*/ */
function adodb_get_gmt_diff() function adodb_get_gmt_diff()
{ {
static $TZ; static $TZ;
if (isset($TZ)) return $TZ; if (isset($TZ)) return $TZ;
// $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0); // $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0);
@ -677,25 +498,6 @@ function adodb_getdate($d=false,$fast=false)
return _adodb_getdate($d); return _adodb_getdate($d);
} }
/*
// generate $YRS table for _adodb_getdate()
function adodb_date_gentable($out=true)
{
for ($i=1970; $i >= 1600; $i-=10) {
$s = adodb_gmmktime(0,0,0,1,1,$i);
echo "$i => $s,<br>";
}
}
adodb_date_gentable();
for ($i=1970; $i > 1500; $i--) {
echo "<hr>$i ";
adodb_date_test_date($i,1,1);
}
*/
/** /**
Low-level function that returns the getdate() array. We have a special Low-level function that returns the getdate() array. We have a special
@ -704,7 +506,7 @@ echo "<hr>$i ";
*/ */
function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) function _adodb_getdate($origd=false,$fast=false,$is_gmt=false)
{ {
static $YRS; static $YRS;
$d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff()); $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff());
@ -767,24 +569,6 @@ static $YRS;
// Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
// //
# old algorithm iterates through all years. new algorithm does it in
# 10 year blocks
/*
# old algo
for ($a = 1970 ; --$a >= 0;) {
$lastd = $d;
if ($leaf = _adodb_is_leap_year($a)) $d += $d366;
else $d += $d365;
if ($d >= 0) {
$year = $a;
break;
}
}
*/
$lastsecs = 0; $lastsecs = 0;
$lastyear = 1970; $lastyear = 1970;
foreach($YRS as $year => $secs) { foreach($YRS as $year => $secs) {

View File

@ -27,6 +27,7 @@ global $conf,$user,$langs,$db;
//define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver //define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver
require_once 'PHPUnit/Autoload.php'; require_once 'PHPUnit/Autoload.php';
require_once dirname(__FILE__).'/../../htdocs/master.inc.php'; require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
require_once dirname(__FILE__).'/../../htdocs/core/lib/date.lib.php';
if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1');
if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1'); if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1');
@ -219,5 +220,35 @@ class FunctionsTest extends PHPUnit_Framework_TestCase
return true; return true;
} }
/**
*/
public function testDolMkTime()
{
$result=dol_mktime(25,0,0,1,1,1970,1,1); // Error (25 hours)
print __METHOD__." result=".$result."\n";
$this->assertEquals('',$result);
$result=dol_mktime(2,61,0,1,1,1970,1,1); // Error (61 minutes)
print __METHOD__." result=".$result."\n";
$this->assertEquals('',$result);
$result=dol_mktime(2,1,61,1,1,1970,1,1); // Error (61 seconds)
print __METHOD__." result=".$result."\n";
$this->assertEquals('',$result);
$result=dol_mktime(2,1,1,1,32,1970,1,1); // Error (day 32)
print __METHOD__." result=".$result."\n";
$this->assertEquals('',$result);
$result=dol_mktime(2,1,1,13,1,1970,1,1); // Error (month 13)
print __METHOD__." result=".$result."\n";
$this->assertEquals('',$result);
$result=dol_mktime(2,1,1,1,1,1970,1); // 1970-01-01 02:01:01 in GMT area -> 7261
print __METHOD__." result=".$result."\n";
$this->assertEquals(7261,$result);
$tz=getCurrentTimeZoneInt();
$result=dol_mktime(2,0,0,1,1,1970,0); // 1970-01-01 02:00:00 in local area
print __METHOD__." result=".$result."\n";
$this->assertEquals(7200-($tz*3600),$result);
}
} }
?> ?>