<?php
  /**************************************************************************\
  * phpGroupWare - Standard DB Query Class                                   *
  * This file written by Miles Lott <milosch@phpgroupware.org>               *
  * Copyright (C) 2001 Miles Lott                                            *
  * -------------------------------------------------------------------------*
  * This library is part of the phpGroupWare API                             *
  * http://www.phpgroupware.org/api                                          * 
  * ------------------------------------------------------------------------ *
  * This library is free software; you can redistribute it and/or modify it  *
  * under the terms of the GNU Lesser General Public License as published by *
  * the Free Software Foundation; either version 2.1 of the License,         *
  * or any later version.                                                    *
  * This library is distributed in the hope that it will be useful, but      *
  * WITHOUT ANY WARRANTY; without even the implied warranty of               *
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
  * See the GNU Lesser General Public License for more details.              *
  * You should have received a copy of the GNU Lesser General Public License *
  * along with this library; if not, write to the Free Software Foundation,  *
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA            *
  \**************************************************************************/

  /* $Id: class.std.inc.php,v 1.6 2001/09/11 12:38:28 milosch Exp $ */

	/*
		This is a simplified query class for simple table lists and
		for fetching single rows of data.  Typical use;

			$tbldef = array(
				'phpgw_some_table', array(
					'id'    => 0,
					'name'  => '',
					'some'  => '',
					'other' => '',
					'info'  => ''
				)
			);
			$obj = CreateObject('appname.std',$tbldef);
			$obj->debug = True;
			$obj->qfields = 'id,name,other';
			$obj->qstr = 'php';
			$local = $obj->get_list();

			...
			var_dump($obj->data);
			var_dump($local);
			...
			single row or list data is returned, but is also set within the
			var, $data.

		By default, array data is serialized within set().  Also,
		checks are done in get() and get_list() to automatically
		unserialize data.  This helps to make this not very efficient,
		but the goal here is simplicity for list data, etc. The (de)serialization
		is switchable.
	*/

	class std
	{
		/* DB Object */
		var $db    = '';
		/* Table name */
		var $table = '';
		/* get or set the current working row id */
		var $id    = 0;
		/* total records as set by get_list() */
		var $total = 0;
		/* global debug flag */
		var $debug = False;

		/* Fields for the table */
		var $fields = array();
		/* Your working data - set to be a copy of $fields at class instantiation,
			but is then overwritten by the result rowset. */
		var $data = array();
		/* Comma-delimited string of fields to query (optional, overrides
			the complete field listing) */
		var $qfields = '';
		/* string for search, (A query may also be passed to get_list(),
			in which case this is ignored.) */
		var $qstr    = '';

		function std($tbldef='')
		{
			/* Set the db object */
			$this->db = $GLOBALS['phpgw']->db;

			/* check for tablename */
			if (gettype($tbldef) == 'array')
			{
				/* Set the table name */
				$this->table  = $tbldef[0];
				if(gettype($tbldef[1]) == 'array')
				{
					/* Set the field list */
					$this->_debug('<br>new class, setting $fields to:' . $tbldef[1]);
					$this->fields = $tbldef[1];
					/* Make a copy */
					$this->data = $this->fields;
				}
			}
		}

		function get($id=0,$unserial=True)
		{
			$fields = $this->_get_fields();
			$fieldstr = implode(',',$fields);
			if (!$id)
			{
				$id = $this->id;
			}

			$sql = "SELECT " . $fieldstr . " FROM " . $this->table . " WHERE id=" . $id;

			$this->_debug('<br>get(): ' . $sql);

			$this->db->query($sql,__LINE__,__FILE__);
			$this->db->next_record();

			@reset($fields);
			while(list($a,$b) = each($fields))
			{
				$c = unserialize($this->db->f($b));
				$d = gettype($c);
				if(($d == 'array' || $d == 'object') && $unserial)
				{
					$e = $c;
				}
				else
				{
					$e = $this->db->f($b);
				}
				$this->data[$this->db->f('id')][$b] = $e;
			}
			return $this->data;
		}

		function set($id=0,$serial=True)
		{
			$fields = $this->_get_fields();
			$fieldstr = implode(',',$fields);

			if($id)
			{
				$sql = "UPDATE " . $this->table . " SET ";
				while(list($a,$b) = each($this->data))
				{
					if ($a != 'id' && gettype($a) != 'integer')
					{
						if((gettype($b) == 'array') && $serial)
						{
							$b = serialize($c);
						}
						$sql .= $a . "='" . $b . "',";
					}
				}
				$sql = substr($sql,0,-1) . " WHERE id=" . $id;
			}
			else
			{
				$values = "'" . implode("','",$this->data) . "'";
				$sql = "INSERT INTO " . $this->table . "(" . $fieldstr . ") VALUES (" . $values . ")";
			}
			$ret = $this->db->query($sql,__LINE__,__FILE__);
			return $ret;
		}

		function get_list($start=0,$query='',$sort='ASC',$order='',$limit=True,$unserial=True)
		{
			$fields = $this->_get_fields();
			$fieldstr = implode(',',$fields);

			if($query || $this->qstr)
			{
				if($query)
				{
					$querystr = $this->_makequery($query);
				}
				elseif($this->qstr)
				{
					$querystr = $this->_makequery($this->qstr);
				}
			}

			$sql = "SELECT " . $fieldstr . " FROM " . $this->table . $querystr;

			if($sort && $order)
			{
				$sort = ' ORDER BY ' . $order . ' ' . $sort . ' ';
			}
			else
			{
				$sort = '';
			}

			if($limit)
			{
				$qfunc = '$this->db->limit_query("' . $sql . $sort . '",' . $start . ',__LINE__,__FILE__);';
			}
			else
			{
				$qfunc = '$this->db->query("' . $sql . $sort . '",__LINE__,__FILE__);';
			}

			$this->db->query($sql,__LINE__,__FILE__);
			$this->total = $this->db->num_rows();

			eval($qfunc);

			$this->_debug('<br>get_list(): ' . $sql);

			if($this->db->num_rows())
			{
				$this->data = array();
			}
			while ($this->db->next_record())
			{
				@reset($fields);
				while(list($a,$b) = @each($fields))
				{
					$c = unserialize($this->db->f($b));
					$d = gettype($c);
					if(($d == 'array' || $d == 'object') && $unserial)
					{
						$e = $c;
					}
					else
					{
						$e = $this->db->f($b);
					}
					$this->_debug('<br>get_list()$this->data[' . $this->db->f('id') . '][' . $b . '] is : ',$e);
					$this->data[$this->db->f('id')][$b] = $e;
				}
			}
			return $this->data;
		}

		/* Return a selectbox */
		function formatted_list($id=0,$name='',$java=True)
		{
			if (!$name)
			{
				return False;
			}
			$list = $this->get_list(0,'','ASC','',False);

			if ($java)
			{
				$jselect = ' onChange="this.form.submit();"';
			}

			$select  = "\n" .'<select name="newval[' . $name . ']"' . $jselect . ">\n";
			$select .= '<option value="0">' . lang('Please Select') . '</option>'."\n";
			while (list($key,$val) = each($list))
			{
				$select .= '<option value="' . $val['id'] . '"';
				if ($val['id'] == $id)
				{
					$select .= ' selected';
				}
				$select .= '>' . $val['name'] . '</option>'."\n";
			}

			$select .= '</select>'."\n";
			$select .= '<noscript><input type="submit" name="style_select" value="{lang_select}"></noscript>' . "\n";

			return $select;
		}

		/*
			Functions for internal use only below...
		*/

		function _get_fields()
		{
			if($this->qfields)
			{
				$tmp = explode(',',$this->qfields);
				@reset($tmp);
				while(list($key,$val) = @each($tmp))
				{
					$fields[] = $val;
				}
			}
			else
			{
				@reset($this->fields);
				while (list($key,$val) = each($this->fields))
				{
					$fields[] = $key;
				}
			}
			return $fields;
		}

		/* Yes, this sucks, but it at least works... */
		function _makequery($query='')
		{
			if (!$query || $this->qstr)
			{
				return;
			}
			elseif($query)
			{
				$look = $query;
			}
			else
			{
				$look = $this->qstr;
			}

			$s = " WHERE (";

			$fields = $this->_get_fields();
			while (list($key,$val) = each($fields))
			{
				if ($val != 'id')
				{
					$s .= $val . " LIKE '%$look%' OR ";
				}
			}
			$s = substr($s,0,-4);
			$s .= ")";

			return $s;
		}

		function _debug($err,$var='')
		{
			if(!$err)
			{
				return;
			}
			if($this->debug)
			{
				echo $err . '&nbsp;';
				if($var)
				{
					var_dump($var);
				}
			}
		}

		/*!
		@function is_serialized
		@abstract find out if something is already serialized
		@param $data could be almost anything
		*/
		function is_serialized($data)
		{
			/* not totally complete: currently works with strings, arrays, and booleans (update this if more is added) */
		
			 /* FUTURE: detect a serialized data that had addslashes appplied AFTER it was serialized
			 you can NOT unserialize that data until those post-serialization slashes are REMOVED */

			//echo 'is_serialized initial input [' .$data .']<br>';
			//echo 'is_serialized unserialized input [' .unserialize($data) .']<br>';

			if (is_array($data))
			{
				// arrays types are of course not serialized (at least not at the top level)
				// BUT there  may be serialization INSIDE in a sub part
				return False;
			}
			elseif (is_bool($data))
			{
				// a boolean type is of course not serialized
				return False;
			}
			elseif ((is_string($data))
			&& (($data == 'b:0;') || ($data == 'b:1;')) )
			{
				// check for easily identifiable serialized boolean values
				return True;
			}
			elseif ((is_string($data))
			&& (unserialize($data) == False))
			{
				// when you unserialize a normal (not-serialized) string, you get False
				return False;
			}
			elseif ((is_string($data))
			&& (ereg('^s:[0-9]+:"',$data) == True))
			{
				// identify pattern of a serialized string (that did NOT have slashes added AFTER serialization )
				return True;
			}
			elseif ((is_string($data))
			&& (is_array(unserialize($data))))
			{
				// if unserialization produces an array out of a string, it was serialized
				//(ereg('^a:[0-9]+:\{',$data) == True))  also could work
				return True;
			}
			//Best Guess - UNKNOWN / ERROR / NOY YET SUPPORTED TYPE
			elseif (is_string($data))
			{
				return True;
			}
			else
			{
				return False;
			}
		}
	}
?>
