Tag Archives: PHP

The Hashmich Class

require_once ( dirname ( __FILE__ ).'/optionstack.php' );

class Hashmich
{
	var $default_options = array ( 'psep' => '.' );
	var $_key_sym = '@';
	var $_num_sym = '#';

	function Hashmich ( $o = null )
	{
	    return $this -> __construct ( $o = null );
	}

	function __construct ( $o = null )
	{
		$this -> o = new OptionStack ( $this -> default_options );

		if ( $o )
		    $this -> o -> push ( $o );
	}

	function &peek ( &$a, $p, $o = array() )
	{
		$o = $this -> o -> push ( $o );

		$p = explode ( $o [ 'psep' ], $p );

		$this -> o -> pop(); // before return

		if ( $o [ 'refer' ] )
		    return $this -> _peek ( &$a, $p );

		$res = $this -> _peek ( &$a, $p ); // we make a copy
		return $res;
	}

	function &_peek ( &$a, $p, $construct = null )
	{
		$pp = array_shift ( $p );

		if ( !isset ( $a [ $pp ] ) )
		{
			if ( $construct === null )
			    return false;

			if ( count ( $p ) === 0 )
			    $a [ $pp ] = $construct;
			else
			    $a [ $pp ] = array();
		}

		if ( count ( $p ) === 0 )
		    return $a [ $pp ];

		return $this -> _peek ( $a [ $pp ], $p, $construct );
	}

	function poke ( &$a, $p, $value, $o = array() )
	{
		$o = $this -> o -> push ( $o );

		$p = explode ( $o [ 'psep' ], $p );

		$this -> _peek ( &$a, $p, $value );

		$this -> o -> pop();
	}

	// path syntax
	//
	// @     top key
	// #     num index
	// (...) eval
	// []    [] (only valid for constructive operations) <<--- noch nicht vorh.!
	// .     default separator, might get overridden

	function _split_eval ( $p )
	{
		preg_match ( '/([^\(\)]*)([\(\)])?(.*)$/', $p, $t );

		array_shift ( $t );

		return $t;
	}

	function _subst_contextuals ( $s )
	{
		$s = str_replace ( $this -> _key_sym,
		                   $this -> context [ 'key'   ], $s );

		$s = str_replace ( $this -> _num_sym,
		                   $this -> context [ 'n_rec' ], $s );

		return $s;
	}

	function _eval_path_r ( $p, &$remains, $final = true )
	{
	    $remains = '';

	    $t = $this -> _split_eval ( $p );

	    $ret = $t [ 0 ];

	    if ( $t [ 1 ] == '(' )
	    {
		    $r    = $this -> _eval_path_r ( $t [ 2 ], $_remains, false );

		    $res  = $this -> peek ( $this -> context [ 'a' ], $r );

		    if ( $final && is_array ( $res ) && !$ret && !$_remains )
		        return $res;

		    $ret .= $res;

		    $ret .= $this -> _eval_path_r ( $_remains, $remains, false );
	    }
	    elseif ( $t [ 1 ] == ')' )
	        $remains = $t [ 2 ];

	    return $this -> _subst_contextuals ( $ret );
	}


	function _eval_path ( $p ) /// brr geht nicht ohne nen stack oder rest... na denn guten Flug!
	{
	    if ( $ks = $this -> o -> get ( 'key_sym' ) )
	        $this -> _key_sym = $ks;

	    if ( $cs = $this -> o -> get ( 'cnt_sym' ) )
	        $this -> _cnt_sym = $cs;

	    return $this -> _eval_path_r ( $p, $dummy );
	}

	function _callback ( $cb, $subj )
	{
	    if ( is_array ( $cb ) && is_object ( $cb [ 0 ] ) && is_string ( $cb [ 1 ] ) )
	    {
	        $f = $cb [ 1 ];
	        return $cb [ 0 ] -> $f ( $subj );
	    }

	    return $cb ( $subj );
	}

	// target path          source path        callback/path callback/value
	// (@.vars.db_fields)   (@.fieldvalue)
	// => table.sn          => Hirsch
	//

	function xlate ( &$a, $xlation, $o = array() )
	{
		$o = $this -> o -> push ( $o );

		$this -> context [ 'a' ] =& $a;

		if ( $o [ 'into' ] )
		    $target =& $o [ 'into' ];
		else
		    $target = array();

		$this -> context [ 'n_rec' ] = 0;

		foreach ( array_keys ( $a ) as $k )
		{
		    if ( $o [ 'num_keys_only' ] && !is_numeric ( $k ) )
		        continue;

			$this -> context [ 'key'   ] = $k;
			$this -> context [ 'n_tpl' ] = 0;

			foreach ( $xlation as $xl )
			{
				list ( $target_loc, $source_expr, $cb_path, $cb_val ) = $xl;

				$tpath = $this -> _eval_path ( $target_loc  );
				$tval  = $this -> _eval_path ( $source_expr );

				if ( $cb_path !== null )
				    $tpath = $this -> _callback ( $cb_path, $tpath );

				if ( $cb_val  !== null )
				    $tval  = $this -> _callback ( $cb_val,  $tval  );

				$this -> poke ( $target, $tpath, $tval );

	 	        $this -> context [ 'n_tpl' ]++;
			}

		    $this -> context [ 'n_rec' ]++;
		}

		$this -> o -> pop();

		return $target;
	}

	function linearize ( &$a, $o = array() )
	{
	    $target = array();

   		$o = $this -> o -> push ( $o );

   		$this -> _lin ( $target, array(), $a, $this -> o -> get ( 'psep' ) );

		$this -> o -> pop();

		return $target;
	}

	function _lin ( &$t, $p, &$a, $s )
	{
	    foreach ( array_keys ( $a ) as $key )
	    {
	        $pp = $p; $pp[] = $key;

	        if ( is_array ( $a [ $key ] ) )
	            $this -> _lin ( $t, $pp, $a [ $key ], $s );
	        else
	            $t [ implode ( $s, $pp ) ] = $a [ $key ];
	    }
	}
}