source: extensions/EStat/lib/statDB.class.inc.php @ 17760

Last change on this file since 17760 was 17760, checked in by grum, 12 years ago

version 0.1.0b

. fix bug on period tab (empty period is managed)
. fix bug on date/time filter (+set time as mandatory value)

  • Property svn:executable set to *
File size: 32.6 KB
Line 
1<?php
2
3include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCCompress.class.inc.php');
4
5define('ASDF_DELETE_PACKED',   0x01);
6define('ASDF_DELETE_UNPACKED', 0x02);
7
8define('ASDF_EXIST_NO',       0x00);
9define('ASDF_EXIST_PACKED',   0x01);
10define('ASDF_EXIST_UNPACKED', 0x02);
11define('ASDF_EXIST_ALL', ASDF_EXIST_PACKED|ASDF_EXIST_UNPACKED);
12
13define('ASDF_FILE_EXT', '.db');
14define('ASDF_FILE_EXT_ZIP', ASDF_FILE_EXT.'.gz');
15define('ASDF_FILE_ROOT_MONTH', 'M');
16define('ASDF_FILE_ROOT_GLOBAL', 'G');
17
18
19define('ASDF_DB_INFO',      0x01);
20define('ASDF_DB_NUM_ROWS',  0x02);
21define('ASDF_DB_LIST',  0x03);
22
23define('ASDF_DB_TYPE_TABLE', 0x01);
24define('ASDF_DB_TYPE_INDEX', 0x02);
25define('ASDF_DB_TYPE_TRIGGER', 0x03);
26define('ASDF_DB_TYPE_LOG', 0x04);
27define('ASDF_DB_TYPE_NFO', 0x05);
28
29define('ASDF_TRANSAC_COMMIT', 0x01);
30define('ASDF_TRANSAC_ROLLBACK', 0x02);
31
32define('ASDF_GET_ROWS', 0x01);
33define('ASDF_GET_COUNT', 0x02);
34
35define('ASDF_BUILD_DONE', 0x01);
36define('ASDF_BUILD_MISSING', 0x02);
37define('ASDF_BUILD_ALL', ASDF_BUILD_DONE|ASDF_BUILD_MISSING);
38
39define('ASDF_OPEN_READ', 0x01);
40define('ASDF_OPEN_WRITE', 0x02);
41
42define('ASDF_CLOSE_DO_NOTHING', 0x00);
43define('ASDF_CLOSE_DO_PACK', 0x01);
44define('ASDF_CLOSE_DO_DELETE', 0x02);
45
46/**
47 * main class for SQLite files managment
48 */
49class StatDB
50{
51  protected $fileRootName='';
52  protected $fileDir='';
53  protected $fileName='';
54  protected $dbHandle=null;
55  protected $transacOpen=false;
56  protected $ipCountryFile='';
57
58  private $deleteUnpackedWhenClose=ASDF_CLOSE_DO_NOTHING;
59
60  /**
61   * constructor
62   *
63   * @param String $directory: directory where the sqlite file is saved
64   * @param String $fileName: file name (without extension)
65   */
66  public function __construct($directory='', $fileName='')
67  {
68    $this->setDirectory($directory);
69    $this->setFileName($fileName);
70  }
71
72  public function __destruct()
73  {
74   $this->close();
75  }
76
77
78  /**
79   * return informations about the current data file
80   *
81   * @return Array
82   */
83  public function getInfo()
84  {
85    $returned=array(
86      'dirName'       => $this->fileDir,
87      'fileName'      => $this->fileName,
88      'fileExist'     => $this->fileExist(),
89      'open'          => ($this->dbHandle==null)?false:true,
90      'unpackedFile'  => array(
91                            'fileName' => $this->getFileName(ASDF_EXIST_UNPACKED),
92                            'fileSize' => ($this->getFileName(ASDF_EXIST_UNPACKED, true)!='')?filesize($this->getFileName(ASDF_EXIST_UNPACKED)):0
93                          ),
94      'packedFile'    => array(
95                            'fileName' => $this->getFileName(ASDF_EXIST_PACKED),
96                            'fileSize' => ($this->getFileName(ASDF_EXIST_PACKED, true)!='')?filesize($this->getFileName(ASDF_EXIST_PACKED)):0
97                          )
98    );
99
100    if($this->dbHandle!=null)
101    {
102      $tables=$this->getDBInfo(ASDF_DB_TYPE_TABLE, null, ASDF_DB_LIST);
103
104      $returned['nbRecords']=array();
105      foreach($tables as $table)
106      {
107        $returned['nbRecords'][$table] = $this->getDBInfo(ASDF_DB_TYPE_TABLE, $table, ASDF_DB_NUM_ROWS);
108      }
109      $returned['db']=array(
110                        'integrity'    => $this->getPragma('integrity_check'),
111                        'pages'        => $this->getPragma('page_count'),
112                        'pageSize'     => $this->getPragma('page_size'),
113                        'unusedPages'  => $this->getPragma('freelist_count'),
114                        'version(s)'   => $this->getPragma('schema_version'),
115                        'version(u)'   => $this->getPragma('user_version')
116                      );
117
118
119      $logs=$this->getDBInfo(ASDF_DB_TYPE_LOG, null, null);
120      $returned['log']=array();
121      foreach($logs as $log)
122      {
123        $returned['log'][$log['key']] = $log['value'];
124      }
125
126
127      $nfos=$this->getDBInfo(ASDF_DB_TYPE_NFO, null, null);
128      $returned['nfo']=array();
129      foreach($nfos as $nfo)
130      {
131        $returned['nfo'][$nfo['key']] = $nfo['value'];
132      }
133
134    }
135    return($returned);
136  }
137
138  /**
139   * set the directory
140   *
141   * @param String $directory: the directory
142   * @return String: directory set
143   */
144  public function setDirectory($directory)
145  {
146    if(substr($directory,-1)!='/') $directory.='/';
147
148    // check if directory is valid
149    if(file_exists($directory) and !$this->isOpen())
150    {
151      $this->fileDir=$directory;
152    }
153    return($this->fileDir);
154  }
155
156  /**
157   * set the file name
158   *
159   * @param String $fileName: file name
160   * @return String: file name
161   */
162  public function setFileName($fileName)
163  {
164    if($this->fileName!=$fileName)
165    {
166      if($this->close()) $this->fileName=$fileName;
167    }
168    return($this->fileName);
169  }
170
171  /**
172   * return information, if set file exists or not
173   *
174   * @return Integer : 0x00 not exist (ASDF_EXIST_NO)
175   *                   0x01 exist, packed (ASDF_EXIST_PACKED)
176   *                   0x02 exist, unpacked ASDF_EXIST_UNPACKED
177   */
178  public function fileExist()
179  {
180    $returned=ASDF_EXIST_NO;
181    if($this->getFileName(ASDF_EXIST_UNPACKED)!='')
182      $returned=$returned | ASDF_EXIST_UNPACKED;
183
184    if($this->getFileName(ASDF_EXIST_PACKED)!='')
185      $returned=$returned | ASDF_EXIST_PACKED;
186
187    return($returned);
188  }
189
190  /**
191   * set the sqlite file name to retrieve IP country values
192   * $file must exist
193   *
194   * @param String $file
195   * @param Boolean $force: if true, $force the filename affectation even if file doesn't exist
196   * @return String: file name set
197   */
198  public function setIpCountryFile($file, $force=false)
199  {
200    if($force or file_exists($file))
201      $this->ipCountryFile=$file;
202    return($this->ipCountryFile);
203  }
204
205  /**
206   * return the sqlite file name used to retrieve IP country values
207   *
208   * @return String
209   */
210  public function getIpCountryFile()
211  {
212    return($this->ipCountryFile);
213  }
214
215
216  /**
217   * open the filename
218   * if $mode=ASDF_OPEN_WRITE and file doesn't exist, create it; if a packed file
219   * exist, try to unpack the packed file; in this case the unpacked file will be
220   * packed and removed automatically when the process will close.
221   *
222   * if $mode=ASDF_OPEN_READ, and unpacked file doesn't exist, try to unpack
223   * packed file (if exists); in this case the unpacked file will be removed
224   * automatically when the process will close.
225   *
226   * @param Integer $mode: READ or WRITE
227   * @return Boolean: true if opened, otherwise false
228   */
229  public function open($mode=ASDF_OPEN_READ)
230  {
231    $dbFile=$this->getFileName(ASDF_EXIST_UNPACKED, true);
232
233    /*
234     * encountered bug: sometime, an unpacked file stay with a zero size length
235     * in this case, assume to unpack packed file
236     */
237    if($dbFile!='' and filesize($dbFile)==0)
238    {
239      unlink($dbFile);
240      $dbFile='';
241    }
242
243    if($dbFile=='')
244    {
245      // file doesn't exist, check if a packed file exist
246      $dbFileP=$this->getFileName(ASDF_EXIST_PACKED, true);
247      if($dbFileP=='' and $mode==ASDF_OPEN_READ) return(false); //no packed file to open for READ, exit
248
249      if($dbFileP!='')
250      {
251        $this->unpack(); //unpack the file
252        $this->deleteUnpackedWhenClose=ASDF_CLOSE_DO_DELETE; // the unpacked file will be deleted when the process will close
253      }
254    }
255
256    switch($mode)
257    {
258      case ASDF_OPEN_READ:
259        $dbFile=$this->getFileName(ASDF_EXIST_UNPACKED, true);
260        if($dbFile=='') return(false); //big problem with unpacked file!?
261        $this->dbHandle=new SQLite3($dbFile, SQLITE3_OPEN_READONLY);
262        if($this->dbHandle) return(true);
263        break;
264      case ASDF_OPEN_WRITE:
265        $dbFile=$this->getFileName(ASDF_EXIST_UNPACKED);
266        //if file was unpacked, pack it before deleting
267        if($this->deleteUnpackedWhenClose==ASDF_CLOSE_DO_DELETE) $this->deleteUnpackedWhenClose=$this->deleteUnpackedWhenClose|ASDF_CLOSE_DO_PACK;
268        $this->dbHandle=new SQLite3($dbFile, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE);
269
270        if($this->dbHandle) $this->checkSchema();
271        return(true);
272        break;
273    }
274
275    return(false);
276  }
277
278  /**
279   * close the file
280   *
281   * @return Boolean : true if file is closed
282   */
283  public function close()
284  {
285    if($this->dbHandle==null) return(true);
286    $this->stopTransac();
287
288    if($this->dbHandle->close())
289    {
290      if(($this->deleteUnpackedWhenClose&ASDF_CLOSE_DO_PACK)==ASDF_CLOSE_DO_PACK)
291      {
292        $this->pack();
293      }
294
295      if(($this->deleteUnpackedWhenClose&ASDF_CLOSE_DO_DELETE)==ASDF_CLOSE_DO_DELETE)
296      {
297        $this->delete(ASDF_DELETE_UNPACKED);
298      }
299
300      $this->deleteUnpackedWhenClose=ASDF_CLOSE_DO_NOTHING;
301      $this->dbHandle=null;
302      return(true);
303    }
304    return(false);
305  }
306
307  /**
308   * pack the data file in a .gz file
309   *
310   * @return Bool : false if a problem happened
311   */
312  public function pack()
313  {
314    $files=array();
315
316    $file=$this->getFileName(ASDF_EXIST_UNPACKED, true);
317
318    if($file!='')
319      return(GPCCompress::gzip($file, $this->getFileName(ASDF_EXIST_PACKED)));
320    return(false);
321  }
322
323  /**
324   * unpack the .gz file
325   *
326   * @return Boolean : false if an error occurs
327   */
328  public function unpack()
329  {
330    if($this->fileExist() & ASDF_EXIST_PACKED==ASDF_EXIST_PACKED)
331    {
332      $returned=GPCCompress::gunzip($this->getFileName(ASDF_EXIST_PACKED), $this->getFileName(ASDF_EXIST_UNPACKED));
333      /*
334      $dir=dirName(dirName($this->getFileName(ASDF_EXIST_UNPACKED)));
335      $tH=fopen($dir.'/logs.log', 'a');
336      if($tH)
337      {
338        $dest=$this->getFileName(ASDF_EXIST_UNPACKED);
339        fwrite($tH, sprintf("%s - %30s - %10d - %s\n", date('Y-m-d H:i:s'), $dest, filesize($dest), $returned?'y':'n'));
340        fclose($tH);
341      }
342*/
343      return($returned);
344    }
345  }
346
347  /**
348   * delete the file
349   *
350   * $mode can take this option
351   *  - ASDF_DELETE_PACKED : delete packed file if exists
352   *  - ASDF_DELETE_UNPACKED : delete unpacked file if exists
353   * option can be combined with a pipe
354   *
355   * @param Integer $mode : delete mode to apply
356   * @return Integer :
357   */
358  public function delete($mode)
359  {
360    $returned=0;
361    if(($mode & ASDF_DELETE_PACKED)==ASDF_DELETE_PACKED)
362    {
363      if($this->getFileName(ASDF_EXIST_PACKED, true)!='')
364      {
365        if(unlink($this->getFileName(ASDF_EXIST_PACKED))) $returned=ASDF_DELETE_PACKED;
366      }
367    }
368
369    if(($mode & ASDF_DELETE_UNPACKED)==ASDF_DELETE_UNPACKED)
370    {
371      $ok=true;
372      if($this->getFileName(ASDF_EXIST_UNPACKED, true)!='') $ok=unlink($this->getFileName(ASDF_EXIST_UNPACKED));
373      if($ok) $returned=$returned | ASDF_DELETE_UNPACKED;
374    }
375
376    return($returned);
377  }
378
379  /**
380   * return true is the file is open
381   *
382   * @return Boolean
383   */
384  public function isOpen()
385  {
386    return($this->dbHandle!=null);
387  }
388
389  /**
390   * return the file name
391   *
392   * @return String
393   */
394  public function getName()
395  {
396    return($this->fileName);
397  }
398  /**
399   * return the directory
400   *
401   * @return String
402   */
403  public function getDirectory()
404  {
405    return($this->fileDir);
406  }
407
408  /**
409   * return the physical file name
410   *
411   * @param Integer $mode: ask for PACKED or UNPACKED file name
412   * @return String: file name if file exists, otherwise ''
413   */
414  public function getFileName($mode=ASDF_EXIST_UNPACKED, $onlyIfExist=false)
415  {
416    $fileName='';
417    switch($mode)
418    {
419      case ASDF_EXIST_PACKED:
420        $fileName=$this->fileDir.$this->fileName.$this->fileRootName.ASDF_FILE_EXT_ZIP;
421        break;
422      case ASDF_EXIST_UNPACKED:
423        $fileName=$this->fileDir.$this->fileName.$this->fileRootName.ASDF_FILE_EXT;
424        break;
425    }
426
427    if($onlyIfExist)
428    {
429      if($fileName!='' and file_exists($fileName)) return($fileName);
430      return('');
431    }
432    return($fileName);
433  }
434
435  /**
436   * begin a transaction
437   *
438   * usefull for performances when mass update are needed
439   *
440   * @return Boolean: true if the transaction is opened, otherwise false
441   */
442  public function startTransac()
443  {
444    if($this->dbHandle==null or $this->transacOpen) return(false);
445
446    if($this->dbHandle->exec("BEGIN TRANSACTION"))
447    {
448      $this->transacOpen=true;
449      return(true);
450    }
451    return(false);
452  }
453
454  /**
455   * stop an opened transaction
456   *
457   * @param Integer $mode: COMMIT (ASDF_TRANSAC_COMMIT) or ROLLBACK (ASDF_TRANSAC_ROLLBACK) the current transaction
458   * @return Boolean: true if the transaction is opened, otherwise false
459   */
460  public function stopTransac($mode=ASDF_TRANSAC_COMMIT)
461  {
462    if($this->dbHandle==null or !$this->transacOpen) return(false);
463
464    $returned=false;
465
466    switch($mode)
467    {
468      case ASDF_TRANSAC_COMMIT:
469        $returned=$this->dbHandle->exec("COMMIT TRANSACTION");
470        break;
471      case ASDF_TRANSAC_ROLLBACK:
472        $returned=$this->dbHandle->exec("ROLLBACK TRANSACTION");
473        break;
474    }
475
476    if($returned) $this->transacOpen=false;
477    return($returned);
478  }
479
480
481  /**
482   * set a value in the infos tables
483   * if key doesn't exist, insert it
484   *
485   * @param String $domain: domain to update
486   * @param String $key: key to update
487   * @param String $value: value to set
488   * @return Boolean
489   */
490  public function setInfoProperty($domain, $key, $value)
491  {
492    if($this->dbHandle==null) return(false);
493    $sql="REPLACE INTO info (domain, key, value)
494            VALUES('".$this->dbHandle->escapeString($domain)."',
495                   '".$this->dbHandle->escapeString($key)."',
496                   '".$this->dbHandle->escapeString($value)."');";
497    return($this->dbHandle->exec($sql));
498  }
499
500  /**
501   * get a value from the info tables
502   * if key doesn't exist, return null by default
503   *
504   * @param String $domain: domain to update
505   * @param String $key: key to update
506   * @param String $value: default value if not found
507   * @return Boolean
508   */
509  public function getInfoProperty($domain, $key, $value=null)
510  {
511    if($this->dbHandle==null) return($value);
512    $sql="SELECT value
513          FROM info
514          WHERE domain = '".$this->dbHandle->escapeString($domain)."'
515            AND key = '".$this->dbHandle->escapeString($key)."';";
516    $returned=$this->dbHandle->querySingle($sql);
517    if($returned==null) $returned=$value;
518    return($returned);
519  }
520
521  /**
522   * check, create & update the schema of the database
523   *
524   * automatically called when the file is opened
525   *
526   * the table 'info' is created by default
527   */
528  protected function checkSchema()
529  {
530    if($this->dbHandle)
531    {
532      $result=$this->getDBInfo(ASDF_DB_TYPE_TABLE, 'info', ASDF_DB_INFO);
533      if(count($result)==0)
534      {
535        $this->dbHandle->exec(
536            "CREATE TABLE 'info' (
537                'domain' TEXT,
538                'key' TEXT,
539                'value' TEXT
540            );"
541          );
542        $this->dbHandle->exec("CREATE UNIQUE INDEX iInfo ON info ('domain', 'key');");
543
544        $this->dbHandle->exec("
545          INSERT INTO info (domain, key, value) VALUES ('nfo', 'create', '".date('Y-m-d H:i:s')."');
546          INSERT INTO info (domain, key, value) VALUES ('nfo', 'version', '00.00.00');
547
548          INSERT INTO info (domain, key, value) VALUES ('log', 'buildStatPeriod-count', '0');
549        ");
550      }
551      return(true);
552    }
553    return(false);
554  }
555
556  /**
557   * return table infos
558   *
559   * $mode=ASDF_DB_INFO
560   *    return the table structure, one row per field
561   *        'name' => field name
562   *        'type' => field type
563   *
564   * $mode=ASDF_DB_NUM_ROWS
565   *    return the number of rows in the table
566   *
567   * @param Integer $type: ASDF_DB_TYPE_TABLE, ASDF_DB_TYPE_INDEX, ASDF_DB_TYPE_TRIGGER
568   * @param String $name
569   * @param Integer $mode : ASDF_DB_INFO, ASDF_DB_NUM_ROWS
570   * @return :
571   */
572  protected function getDBInfo($type, $name, $mode=0)
573  {
574    if($this->dbHandle==null) return(null);
575
576    switch($type)
577    {
578      case ASDF_DB_TYPE_TABLE:
579          switch($mode)
580          {
581            case ASDF_DB_INFO:
582                $returned=array();
583                $result=$this->dbHandle->query("PRAGMA table_info('$name')");
584                if($result)
585                {
586                  while($row=$result->fetchArray(SQLITE3_ASSOC))
587                  {
588                    $returned[]=array(
589                      'name' => $row['name'],
590                      'type' => $row['type']
591                    );
592                  }
593                }
594              break;
595            case ASDF_DB_NUM_ROWS:
596                $returned=$this->dbHandle->querySingle("SELECT COUNT(*) FROM $name");
597              break;
598            case ASDF_DB_LIST:
599                $returned=array();
600                $result=$this->dbHandle->query("SELECT name FROM sqlite_master WHERE type='table';");
601                if($result)
602                {
603                  while($row=$result->fetchArray(SQLITE3_ASSOC))
604                  {
605                    $returned[]=$row['name'];
606                  }
607                }
608              break;
609          }
610        break;
611      case ASDF_DB_TYPE_INDEX:
612          $returned=$this->dbHandle->querySingle("SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name='$name'");
613        break;
614      case ASDF_DB_TYPE_TRIGGER:
615          $returned=$this->dbHandle->querySingle("SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name='$name'");
616        break;
617      case ASDF_DB_TYPE_LOG:
618          $returned=array();
619          if(count($this->getDBInfo(ASDF_DB_TYPE_TABLE, 'info', ASDF_DB_INFO))==0) return($returned);
620          $result=$this->dbHandle->query("SELECT key, value FROM info WHERE domain='log';");
621          if($result)
622          {
623            while($row=$result->fetchArray(SQLITE3_ASSOC))
624            {
625              $returned[]=$row;
626            }
627          }
628        break;
629      case ASDF_DB_TYPE_NFO:
630          $returned=array();
631          if(count($this->getDBInfo(ASDF_DB_TYPE_TABLE, 'info', ASDF_DB_INFO))==0) return($returned);
632          $result=$this->dbHandle->query("SELECT key, value FROM info WHERE domain='nfo';");
633          if($result)
634          {
635            while($row=$result->fetchArray(SQLITE3_ASSOC))
636            {
637              $returned[]=$row;
638            }
639          }
640        break;
641    }
642
643    return($returned);
644  }
645
646  /**
647   * return pragma info
648   *
649   * @param String $pragma: a sqlite pragma command
650   * @return: if single data is returned, return the data otherwise return an array
651   */
652  protected function getPragma($pragma)
653  {
654    $returned=array();
655
656    if($this->dbHandle==null) return($returned);
657
658    $sql="PRAGMA ".$pragma;
659    $result=$this->dbHandle->query($sql);
660    if($result)
661    {
662      while($row=$result->fetchArray(SQLITE3_ASSOC))
663      {
664        $returned[]=$row;
665      }
666      if(count($returned)==1) $returned=$returned[0][$pragma];
667    }
668    return($returned);
669  }
670
671
672
673  /**
674   * build a where clause from a filter array
675   * used by functions:
676   *  . getLogs()
677   *  . getStatIP()
678   *  . getStatCat()
679   *  . getStatImages()
680   *
681   * return a string:
682   *  . empty '' string if there's no item to filter
683   *  . a ready to use ' WHERE ' clause string
684   *
685   * @param Array $filter: a valid filter
686   * @param Array &$IPList: an array, used to return a ':IPn' list for requests
687   *                        filtering IP adress
688   * @return String
689   */
690  protected function buildWhereClause($filter, &$IPList)
691  {
692    $where=array();
693    $IPList=array();
694    $num=0;
695
696    if(!is_array($filter)) return('');
697
698    foreach($filter as $field => $param)
699    {
700      if(is_array($param))
701      {
702        switch($param['operator'])
703        {
704          case '=':
705          case '>':
706          case '>=':
707          case '<':
708          case '<=':
709          case '!=':
710            if($field=='IPadress')
711            {
712              $IPList[$num]=$param['value'];
713              $param['value']=':IP'.$num;
714              $num++;
715            }
716            $where[]=' '.$field.' '.$param['operator']." ".$param['value']." ";
717            break;
718          case 'in':
719            if($field=='IPadress')
720            {
721              foreach($param['value'] as $key=>$ipAdress)
722              {
723                $IPList[$num]=$ipAdress;
724                $param[$key]['value']=':IP'.$num;
725                $num++;
726              }
727            }
728            $where[]=' '.$field." IN (".implode(',', $param['value']).") ";
729            break;
730          case 'between':
731          case 'not between':
732            if($field=='IPadress')
733            {
734              $IPList[$num]=$param['minValue'];
735              $param['minValue']=':IP'.$num;
736              $num++;
737              $IPList[$num]=$param['maxValue'];
738              $param['maxValue']=':IP'.$num;
739              $num++;
740            }
741            $where[]=' '.$field.' '.strtoupper($param['operator']).' '.$param['minValue']." AND ".$param['maxValue']." ";
742            break;
743        }
744      }
745    }
746
747    if(count($where)>0)
748    {
749      $where=" WHERE ".implode(' AND ', $where)." ";
750    }
751    else
752    {
753      $where='';
754    }
755
756    return($where);
757  }
758
759
760
761  /**
762   * build an ORDER BY clause
763   * used by functions:
764   *  . getLogs()
765   *  . getStatIP()
766   *  . getStatCat()
767   *  . getStatImages()
768   *
769   * return a string:
770   *  . empty '' string if there's no limits
771   *  . a ready to use ' ORDER BY ' clause string
772   *
773   * @param Array $filter: a valid order by list
774   * @return String
775   */
776  protected function buildOrderByClause($orderBy)
777  {
778    $order=array();
779    foreach($orderBy as $sort)
780    {
781      $order[]=' '.$sort['id'].' '.($sort['direction']=='A'?'ASC':'DESC');
782    }
783
784    if(count($order)>0)
785    {
786      $orderBy=" ORDER BY ".implode(',', $order);
787    }
788    else
789    {
790      $orderBy='';
791    }
792
793    return($orderBy);
794  }
795
796
797
798  /**
799   * build a LIMIT clause
800   * used by functions:
801   *  . getLogs()
802   *  . getStatIP()
803   *  . getStatCat()
804   *  . getStatImages()
805   *
806   * return a string:
807   *  . empty '' string if there's no limits
808   *  . a ready to use ' LIMIT x,y ' clause string
809   *
810   * @param Integer $page: page number
811   * @param Integer $nbItemsPage: number of items per page
812   * @return String
813   */
814  protected function buildLimitClause($page, $nbItemsPage)
815  {
816    if($page<=0 or $nbItemsPage<=0)
817    {
818      $limit='';
819    }
820    else
821    {
822      $limit=' LIMIT '.(($page-1)*$nbItemsPage).', '.$nbItemsPage;
823    }
824
825    return($limit);
826  }
827
828
829  /**
830   * to be used with IP filter: replace the given operator with 'between' or 'not between'
831   *
832   * @param Array $value
833   * @return Array
834   */
835  protected function checkIPFilter($value)
836  {
837    if(is_array($value))
838    {
839      if(isset($value['operator']))
840      {
841        if($value['operator']=='=' or
842           $value['operator']=='>' or
843           $value['operator']=='>=' or
844           $value['operator']=='<' or
845           $value['operator']=='<=' or
846           $value['operator']=='like')
847        {
848          $value['operator']='between';
849          $value['minValue']=$value['value'];
850          $value['maxValue']=$value['value'];
851          $value['value']=null;
852        }
853        elseif($value['operator']=='!=' or
854               $value['operator']=='not like')
855        {
856          $value['operator']='not between';
857          $value['minValue']=$value['value'];
858          $value['maxValue']=$value['value'];
859          $value['value']=null;
860        }
861      }
862    }
863    return($value);
864  } // checkIPFilter
865
866
867
868  /**
869   * to be used with Date filter: replace the given operator with 'between' or 'not between'
870   *
871   * @param Array $value
872   * @return Array
873   */
874  protected function checkDateFilter($value)
875  {
876    if(is_array($value))
877    {
878      if(isset($value['operator']))
879      {
880        if($value['operator']=='=' or
881           $value['operator']=='like')
882        {
883          $value['operator']='between';
884          $value['minValue']=$value['value'];
885          $value['maxValue']=$value['value'];
886          $value['value']=null;
887        }
888        elseif($value['operator']=='!=' or
889               $value['operator']=='not like')
890        {
891          $value['operator']='not between';
892          $value['minValue']=$value['value'];
893          $value['maxValue']=$value['value'];
894          $value['value']=null;
895        }
896      }
897    }
898    return($value);
899  } // checkDateFilter
900
901
902
903
904  /**
905   * check if value is a valid operator array
906   * if not, return a null value otherwise return the operator
907   *
908   * validity of value are checked according to the format; valid format are:
909   *  - 'date'    => 'YYYY-MM-DD (HH:II(:SS))'
910   *  - 'IP'      => 'xxx.xxx.xxx.xxx' for IPv4, 'xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx' for IPv6
911   *  - 'string'  => a string
912   *  - 'integer' => any positive integer value
913   *
914   * @param Array $value
915   * @param String $format
916   * @return Array:
917   */
918  protected function getOperator($value, $format)
919  {
920    $returned=null;
921    if($value==null) return(null);
922    if(is_array($value))
923    {
924      if(isset($value['operator']))
925      {
926        switch($value['operator'])
927        {
928          case '=':
929          case '>':
930          case '>=':
931          case '<':
932          case '<=':
933          case '!=':
934          case 'like':
935          case 'not like':
936            if(isset($value['value']))
937            {
938              switch($format)
939              {
940                case 'date':
941                  switch($value['operator'])
942                  {
943                    case '>':
944                    case '<=':
945                      $value['value']=$this->checkValueDate($value['value'], 'E');
946                      break;
947                    case '<':
948                    case '>=':
949                    default:
950                      $value['value']=$this->checkValueDate($value['value'], 'B');
951                      break;
952                  }
953                  if($value['value']==null) return(null);
954                  $value['value']=$value['value'];
955                  break;
956                case 'IP':
957                  // for IP, replace internaly the operator with a 'between'
958                  $value['value']=$this->checkValueIP($value['value'], 0);
959                  if($value['value']==null) return(null);
960                  break;
961                case 'string':
962                  if(!is_string($value['value']) or $value['value']=='') return(null);
963                  $value['value']="'".$value['value']."'";
964                  break;
965                case 'integer':
966                  if(!is_int($value['value']) or $value['value']<0) return(null);
967                  break;
968              }
969              $returned=$value;
970            }
971            break;
972          case 'in':
973            if(isset($value['value']) and is_array($value['value']))
974            {
975              $returned=array();
976              foreach($value['value'] as $val)
977              {
978                switch($format)
979                {
980                  case 'date':
981                    $val=$this->checkValueDate($val);
982                    if($val!=null) $returned[]=$val;
983                    break;
984                  case 'IP':
985                    $val=$this->checkValueIP($val, 0);
986                    if($val!=null) $returned[]=$val;
987                    break;
988                  case 'string':
989                    if(is_string($val) and $val!='') $returned[]="'".$val."'";
990                    break;
991                  case 'integer':
992                    if(is_int($val*1) and $val>=0) $returned[]=$val;
993                    break;
994                }
995              }
996              if(count($returned)==0) return(null);
997              $value['value']=$returned;
998              $returned=$value;
999            }
1000            break;
1001          case 'between':
1002          case 'not between':
1003            if(isset($value['minValue']) and  isset($value['maxValue']))
1004            {
1005              switch($format)
1006              {
1007                case 'date':
1008                  $value['minValue']=$this->checkValueDate($value['minValue'], 'B');
1009                  if($value['minValue']==null) return(null);
1010                  $value['maxValue']=$this->checkValueDate($value['maxValue'], 'E');
1011                  if($value['maxValue']==null) return(null);
1012                  break;
1013                case 'IP':
1014                  $value['minValue']=$this->checkValueIP($value['minValue'], 0);
1015                  if($value['minValue']==null) return(null);
1016                  $value['maxValue']=$this->checkValueIP($value['maxValue'], 255);
1017                  if($value['maxValue']==null) return(null);
1018                  break;
1019                case 'string':
1020                  if(!is_string($value['minValue']) or $value['minValue']=='' or
1021                     !is_string($value['maxValue']) or $value['maxValue']=='') return(null);
1022
1023                  $value['minValue']="'".$value['minValue']."'";
1024                  $value['maxValue']="'".$value['maxValue']."'";
1025                  break;
1026                case 'integer':
1027                  if(!is_int($value['minValue']) or $value['minValue']<0 or
1028                     !is_int($value['maxValue']) or $value['maxValue']<0) return(null);
1029                  break;
1030              }
1031              if($value['minValue']>$value['maxValue']) return(null);
1032              $returned=$value;
1033            }
1034            break;
1035        }
1036      }
1037    }
1038    return($returned);
1039  }
1040
1041  /**
1042   * check if date is Ok
1043   * return the date completed
1044   *      (YYYY-MM-DD => YYYY-MM-DD 00:00:00) if $mode='B'
1045   *  or  (YYYY-MM-DD => YYYY-MM-DD 23:59:59) if $mode='E'
1046   *  or  null if not valid
1047   *
1048   * @param String $value
1049   * @param String $mode: 'B' for begin or 'E' for end
1050   * @return String
1051   */
1052  protected function checkValueDate($value, $mode='B')
1053  {
1054    $returned=null;
1055    $result=array();
1056    if(preg_match('/^(\d{4}-\d{2}-\d{2})(?:(\s{1}\d{2}:\d{2})(:\d{2})?)?$/', trim($value), $result)>0)
1057    {
1058      $returned=$result[1];
1059      if(isset($result[2]))
1060      {
1061        $returned.=$result[2];
1062        if(isset($result[3]))
1063        {
1064          $returned.=$result[3];
1065        }
1066        else
1067        {
1068          $returned.=($mode=='B')?':00':':59';
1069        }
1070      }
1071      else
1072      {
1073        $returned.=($mode=='B')?' 00:00:00':' 23:59:59';
1074      }
1075    }
1076    return(strtotime($returned));
1077  }
1078
1079  /**
1080   * check if IP is Ok (IPv4 or IPv6)
1081   * return the IP or null
1082   *
1083   * @param String $value
1084   * @param Integer $completeCode
1085   * @return String
1086   */
1087  protected function checkValueIP($value, $completeCode=0)
1088  {
1089    $returned=self::IPBinaryEncode($value, $completeCode);
1090    if($returned=="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") return(null);
1091    return($returned);
1092  }
1093
1094
1095
1096
1097
1098
1099
1100  /**
1101   * return an IP adress with format 'xxx.xxx.xxx.xxx' as a binary encoded string
1102   *
1103   * @param String $IP: text IP string
1104   * @param Integer $completeCode
1105   * @return String: a binary string
1106   */
1107  static public function IPBinaryEncode($IP, $completeCode=0)
1108  {
1109    $value=trim($IP);
1110    if(preg_match('/\d{1,3}(\.\d{1,3}(\.\d{1,3}(\.\d{1,3})?)?)?/i',$value)>0)
1111    {
1112      //IPv4
1113      $tmp=explode('.',$value);
1114      while(count($tmp)<4) $tmp[]=$completeCode;
1115
1116      return("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".chr($tmp[0]+0).chr($tmp[1]+0).chr($tmp[2]+0).chr($tmp[3]+0));
1117    }
1118    elseif(preg_match('/[a-f0-9]{0,4}:[a-f0-9]{0,4}:[a-f0-9]{0,4}:[a-f0-9]{0,4}:[a-f0-9]{0,4}:[a-f0-9]{0,4}:[a-f0-9]{0,4}:[a-f0-9]{0,4}/i',$value))
1119    {
1120      //IPv6
1121    }
1122    else
1123    {
1124      return("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
1125    }
1126  }
1127
1128  /**
1129   * return an IP adress with format 'xxx.xxx.xxx.xxx' from a binary encoded string
1130   *
1131   * @param String $IP: binary encoded string
1132   * @return String: a text IP string
1133   */
1134  static public function IPBinaryDecode($IP)
1135  {
1136    $returned='';
1137    if(strlen($IP)==16)
1138    {
1139      if(substr($IP,0,12)=="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
1140      {
1141        //IPv4
1142        $returned=sprintf('%d.%d.%d.%d', ord(substr($IP,12,1)), ord(substr($IP,13,1)), ord(substr($IP,14,1)), ord(substr($IP,15,1)));
1143      }
1144      else
1145      {
1146        //IPv6
1147      }
1148    }
1149    else if(strlen($IP)==4)
1150    {
1151      $returned=sprintf('%d.%d.%d.%d', ord(substr($IP,0,1)), ord(substr($IP,1,1)), ord(substr($IP,2,1)), ord(substr($IP,3,1)));
1152    }
1153    else
1154    {
1155    }
1156    return($returned);
1157  }
1158
1159
1160  /**
1161    * Convert the given data into an unsigned Short integer (16bit)
1162    *
1163    * @param String $data       : representing the unsigned Short
1164    * @param String $endianType : the byte order
1165    * @return UShort
1166    */
1167  static public function toUShort($data)
1168  {
1169    if(strlen($data)>=2)
1170      return(ord($data{0})*256 + ord($data{1}));
1171    return(0);
1172  }
1173
1174  /**
1175    * Convert the given data into an unsigned Long integer (32bit)
1176    *
1177    * @param String $data       : representing the unsigned Long
1178    * @param String $endianType : the byte order
1179    * @return ULong
1180    */
1181  static public function toULong($data)
1182  {
1183    if(strlen($data)>=4)
1184        return(ord($data{0})*16777216 +
1185                ord($data{1})*65536 +
1186                ord($data{2})*256 +
1187                ord($data{3}));
1188    return(0);
1189  }
1190
1191
1192}
1193
1194
1195/*
1196 used to display binary values when debug is necessary...
1197
1198function toHexDump($data)
1199  {
1200    $returned="";
1201    $maxItems=strlen($data);
1202
1203    $tmp=array();
1204    for($i=0;$i<$maxItems;$i++)
1205    {
1206      $tmp[]=sprintf("%02x", ord($data{$i}));
1207    }
1208    $returned.=implode(" ", $tmp);
1209    return($returned);
1210  }
1211*/
1212
1213
1214
1215
1216?>
Note: See TracBrowser for help on using the repository browser.