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

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

version 0.1.0b

. Fix install bugs
. Manage anonymous directories
. Manage CSV export options settings
. Fix IPadress<=>country association bug & improve join performances
. Fix bug on IP filter
. Improve performances for history consult

  • Property svn:executable set to *
File size: 31.1 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   * check if value is a valid operator array
869   * if not, return a null value otherwise return the operator
870   *
871   * validity of value are checked according to the format; valid format are:
872   *  - 'date'    => 'YYYY-MM-DD (HH:II(:SS))'
873   *  - 'IP'      => 'xxx.xxx.xxx.xxx' for IPv4, 'xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx' for IPv6
874   *  - 'string'  => a string
875   *  - 'integer' => any positive integer value
876   *
877   * @param Array $value
878   * @param String $format
879   * @return Array:
880   */
881  protected function getOperator($value, $format)
882  {
883    $returned=null;
884    if($value==null) return(null);
885    if(is_array($value))
886    {
887      if(isset($value['operator']))
888      {
889        switch($value['operator'])
890        {
891          case '=':
892          case '>':
893          case '>=':
894          case '<':
895          case '<=':
896          case '!=':
897          case 'like':
898          case 'not like':
899            if(isset($value['value']))
900            {
901              switch($format)
902              {
903                case 'date':
904                  $value['value']=$this->checkValueDate($value['value']);
905                  if($value['value']==null) return(null);
906                  $value['value']=$value['value'];
907                  break;
908                case 'IP':
909                  // for IP, replace internaly the operator with a 'between'
910                  $value['value']=$this->checkValueIP($value['value'], 0);
911                  if($value['value']==null) return(null);
912                  break;
913                case 'string':
914                  if(!is_string($value['value']) or $value['value']=='') return(null);
915                  $value['value']="'".$value['value']."'";
916                  break;
917                case 'integer':
918                  if(!is_int($value['value']) or $value['value']<0) return(null);
919                  break;
920              }
921              $returned=$value;
922            }
923            break;
924          case 'in':
925            if(isset($value['value']) and is_array($value['value']))
926            {
927              $returned=array();
928              foreach($value['value'] as $val)
929              {
930                switch($format)
931                {
932                  case 'date':
933                    $val=$this->checkValueDate($val);
934                    if($val!=null) $returned[]=$val;
935                    break;
936                  case 'IP':
937                    $val=$this->checkValueIP($val, 0);
938                    if($val!=null) $returned[]=$val;
939                    break;
940                  case 'string':
941                    if(is_string($val) and $val!='') $returned[]="'".$val."'";
942                    break;
943                  case 'integer':
944                    if(is_int($val*1) and $val>=0) $returned[]=$val;
945                    break;
946                }
947              }
948              if(count($returned)==0) return(null);
949              $value['value']=$returned;
950              $returned=$value;
951            }
952            break;
953          case 'between':
954          case 'not between':
955            if(isset($value['minValue']) and  isset($value['maxValue']))
956            {
957              switch($format)
958              {
959                case 'date':
960                  $value['minValue']=$this->checkValueDate($value['minValue']);
961                  if($value['minValue']==null) return(null);
962                  $value['maxValue']=$this->checkValueDate($value['maxValue']);
963                  if($value['maxValue']==null) return(null);
964                  break;
965                case 'IP':
966                  $value['minValue']=$this->checkValueIP($value['minValue'], 0);
967                  if($value['minValue']==null) return(null);
968                  $value['maxValue']=$this->checkValueIP($value['maxValue'], 255);
969                  if($value['maxValue']==null) return(null);
970                  break;
971                case 'string':
972                  if(!is_string($value['minValue']) or $value['minValue']=='' or
973                     !is_string($value['maxValue']) or $value['maxValue']=='') return(null);
974
975                  $value['minValue']="'".$value['minValue']."'";
976                  $value['maxValue']="'".$value['maxValue']."'";
977                  break;
978                case 'integer':
979                  if(!is_int($value['minValue']) or $value['minValue']<0 or
980                     !is_int($value['maxValue']) or $value['maxValue']<0) return(null);
981                  break;
982              }
983              if($value['minValue']>$value['maxValue']) return(null);
984              $returned=$value;
985            }
986            break;
987        }
988      }
989    }
990    return($returned);
991  }
992
993  /**
994   * check if date is Ok
995   * return the date completed (YYYY-MM-DD => YYYY-MM-DD 00:00:00) or null if not valid
996   *
997   * @param String $value
998   * @return String
999   */
1000  protected function checkValueDate($value)
1001  {
1002    $returned=null;
1003    $result=array();
1004    if(preg_match('/^(\d{4}-\d{2}-\d{2})(?:(\s{1}\d{2}:\d{2})(:\d{2})?)?$/', $value, $result)>0)
1005    {
1006      $returned=$result[1];
1007      if(isset($result[2]))
1008      {
1009        $returned.=$result[2];
1010        if(isset($result[3]))
1011        {
1012          $returned.=$result[3];
1013        }
1014        else $returned.=':00';
1015      }
1016      else $returned.=' 00:00:00';
1017    }
1018    return(strtotime($returned));
1019  }
1020
1021  /**
1022   * check if IP is Ok (IPv4 or IPv6)
1023   * return the IP or null
1024   *
1025   * @param String $value
1026   * @param Integer $completeCode
1027   * @return String
1028   */
1029  protected function checkValueIP($value, $completeCode=0)
1030  {
1031    $returned=self::IPBinaryEncode($value, $completeCode);
1032    if($returned=="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") return(null);
1033    return($returned);
1034  }
1035
1036
1037
1038
1039
1040
1041
1042  /**
1043   * return an IP adress with format 'xxx.xxx.xxx.xxx' as a binary encoded string
1044   *
1045   * @param String $IP: text IP string
1046   * @param Integer $completeCode
1047   * @return String: a binary string
1048   */
1049  static public function IPBinaryEncode($IP, $completeCode=0)
1050  {
1051    $value=trim($IP);
1052    if(preg_match('/\d{1,3}(\.\d{1,3}(\.\d{1,3}(\.\d{1,3})?)?)?/i',$value)>0)
1053    {
1054      //IPv4
1055      $tmp=explode('.',$value);
1056      while(count($tmp)<4) $tmp[]=$completeCode;
1057
1058      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));
1059    }
1060    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))
1061    {
1062      //IPv6
1063    }
1064    else
1065    {
1066      return("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
1067    }
1068  }
1069
1070  /**
1071   * return an IP adress with format 'xxx.xxx.xxx.xxx' from a binary encoded string
1072   *
1073   * @param String $IP: binary encoded string
1074   * @return String: a text IP string
1075   */
1076  static public function IPBinaryDecode($IP)
1077  {
1078    $returned='';
1079    if(strlen($IP)==16)
1080    {
1081      if(substr($IP,0,12)=="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
1082      {
1083        //IPv4
1084        $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)));
1085      }
1086      else
1087      {
1088        //IPv6
1089      }
1090    }
1091    else if(strlen($IP)==4)
1092    {
1093      $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)));
1094    }
1095    else
1096    {
1097    }
1098    return($returned);
1099  }
1100
1101
1102  /**
1103    * Convert the given data into an unsigned Short integer (16bit)
1104    *
1105    * @param String $data       : representing the unsigned Short
1106    * @param String $endianType : the byte order
1107    * @return UShort
1108    */
1109  static public function toUShort($data)
1110  {
1111    if(strlen($data)>=2)
1112      return(ord($data{0})*256 + ord($data{1}));
1113    return(0);
1114  }
1115
1116  /**
1117    * Convert the given data into an unsigned Long integer (32bit)
1118    *
1119    * @param String $data       : representing the unsigned Long
1120    * @param String $endianType : the byte order
1121    * @return ULong
1122    */
1123  static public function toULong($data)
1124  {
1125    if(strlen($data)>=4)
1126        return(ord($data{0})*16777216 +
1127                ord($data{1})*65536 +
1128                ord($data{2})*256 +
1129                ord($data{3}));
1130    return(0);
1131  }
1132
1133
1134}
1135
1136
1137/*
1138 used to display binary values when debug is necessary...
1139
1140function toHexDump($data)
1141  {
1142    $returned="";
1143    $maxItems=strlen($data);
1144
1145    $tmp=array();
1146    for($i=0;$i<$maxItems;$i++)
1147    {
1148      $tmp[]=sprintf("%02x", ord($data{$i}));
1149    }
1150    $returned.=implode(" ", $tmp);
1151    return($returned);
1152  }
1153*/
1154
1155
1156
1157
1158?>
Note: See TracBrowser for help on using the repository browser.