PHPLinq
[ class tree: PHPLinq ] [ index: PHPLinq ] [ all elements ]

Source for file LinqToZendDb.php

Documentation is available at LinqToZendDb.php

  1. <?php
  2. /**
  3.  * PHPLinq
  4.  *
  5.  * Copyright (c) 2008 - 2009 PHPLinq
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20.  *
  21.  * @category   PHPLinq
  22.  * @package    PHPLinq
  23.  * @copyright  Copyright (c) 2008 - 2009 PHPLinq (http://www.codeplex.com/PHPLinq)
  24.  * @license    http://www.gnu.org/licenses/lgpl.txt    LGPL
  25.  * @version    0.4.0, 2009-01-27
  26.  */
  27.  
  28.  
  29. /** PHPLinq */
  30. require_once('PHPLinq.php');
  31.  
  32. /** PHPLinq_ILinqProvider */
  33. require_once('PHPLinq/ILinqProvider.php');
  34.  
  35. /** PHPLinq_LinqToObjects */
  36. require_once('PHPLinq/LinqToObjects.php');
  37.  
  38. /** PHPLinq_Expression */
  39. require_once('PHPLinq/Expression.php');
  40.  
  41. /** PHPLinq_OrderByExpression */
  42. require_once('PHPLinq/OrderByExpression.php');
  43.  
  44. /** PHPLinq_Initiator */
  45. require_once('PHPLinq/Initiator.php');
  46.  
  47. /** PHPLinq_Adapter_Abstract */
  48. require_once('PHPLinq/Adapter/Abstract.php');
  49.  
  50. /** Zend_Db_Table */
  51. require_once('Zend/Db/Table.php');
  52.  
  53. /** Register ILinqProvider */
  54. PHPLinq_Initiator::registerProvider('PHPLinq_LinqToZendDb');
  55.  
  56.  
  57. /**
  58.  * PHPLinq_LinqToZendDb
  59.  *
  60.  * @category   PHPLinq
  61.  * @package    PHPLinq
  62.  * @copyright  Copyright (c) 2008 - 2009 PHPLinq (http://www.codeplex.com/PHPLinq)
  63.  */
  64. class PHPLinq_LinqToZendDb implements PHPLinq_ILinqProvider {
  65.     /** Constants */
  66.     const T_FUNCTION         1001001;
  67.     const T_PROPERTY         1001002;
  68.     const T_CONSTANT         1001003;
  69.     const T_VARIABLE         1001004;
  70.     const T_OBJECT_OPERATOR 1001005;
  71.     const T_OPERATOR        1001006;
  72.     const T_OPERAND         1001007;
  73.     const T_DEFAULT         1001008;
  74.     const T_START_STOP         1001009;
  75.     const T_SIMPLE            1001010;
  76.     const T_ARGUMENT        1001011;
  77.     const T_ARITHMETIC        1001012;
  78.     
  79.     /**
  80.      * Default variable name
  81.      *
  82.      * @var string 
  83.      */
  84.     private $_from = '';
  85.     
  86.     /**
  87.      * Data source
  88.      *
  89.      * @var mixed 
  90.      */
  91.     private $_data = null;
  92.     
  93.     /**
  94.      * Where expression
  95.      *
  96.      * @var PHPLinq_Expression 
  97.      */
  98.     private $_where = null;
  99.     
  100.     /**
  101.      * Take n elements
  102.      *
  103.      * @var int? 
  104.      */
  105.     private $_take = null;
  106.     
  107.     /**
  108.      * Skip n elements
  109.      *
  110.      * @var int? 
  111.      */
  112.     private $_skip = null;
  113.     
  114.     /**
  115.      * Take while expression is true
  116.      *
  117.      * @var PHPLinq_Expression 
  118.      */
  119.     private $_takeWhile = null;
  120.     
  121.     /**
  122.      * Skip while expression is true
  123.      *
  124.      * @var PHPLinq_Expression 
  125.      */
  126.     private $_skipWhile = null;
  127.     
  128.     /**
  129.      * OrderBy expressions
  130.      *
  131.      * @var PHPLinq_Expression[] 
  132.      */
  133.     private $_orderBy = array();
  134.     
  135.     /**
  136.      * Distinct expression
  137.      *
  138.      * @var PHPLinq_Expression 
  139.      */
  140.     private $_distinct = null;
  141.     
  142.     /**
  143.      * OfType expression
  144.      *
  145.      * @var PHPLinq_Expression 
  146.      */
  147.     private $_ofType = null;
  148.     
  149.     /**
  150.      * Parent PHPLinq_ILinqProvider instance, used with join conditions
  151.      *
  152.      * @var PHPLinq_ILinqProvider 
  153.      */
  154.     private $_parentProvider = null;
  155.     
  156.     /**
  157.      * Child PHPLinq_ILinqProvider instances, used with join conditions
  158.      *
  159.      * @var PHPLinq_ILinqProvider[] 
  160.      */
  161.     private $_childProviders = array();
  162.     
  163.     /**
  164.      * Join condition
  165.      *
  166.      * @var PHPLinq_Expression 
  167.      */
  168.     private $_joinCondition = null;
  169.     
  170.     /**
  171.      * Is object destructing?
  172.      *
  173.      * @var bool 
  174.      */
  175.     private $_isDestructing;
  176.     
  177.     /**
  178.      * PHPLinq_Adapter_Abstract instance
  179.      *
  180.      * @var PHPLinq_Adapter_Abstract 
  181.      */
  182.     private $_adapter = null;
  183.     
  184.     /**
  185.      * Columns to select
  186.      *
  187.      * @var string 
  188.      */
  189.     private $_columns = '*';
  190.     
  191.     /**
  192.      * Query callback (static for all PHPLinq_LinqToZendDb !)
  193.      * 
  194.      * Function accepting a string to which query strings can be logged.
  195.      *
  196.      * @var mixed 
  197.      */
  198.     private static $_queryCallback null;
  199.     
  200.     /**
  201.      * Static list of PHP internal functions used for generating queries
  202.      *
  203.      * @var array 
  204.      */
  205.     private static $_internalFunctions null;
  206.     
  207.     /**
  208.      * Can this provider type handle data in $source?
  209.      *
  210.      * @param mixed $source 
  211.      * @return bool 
  212.      */
  213.     public static function handles($source{
  214.         return $source instanceof Zend_Db_Table;
  215.     }
  216.     
  217.     /**
  218.      * Create a new class instance
  219.      *
  220.      * @param string $name 
  221.      * @param PHPLinq_ILinqProvider $parentProvider Optional parent PHPLinq_ILinqProvider instance, used with join conditions
  222.      * @return PHPLinq_ILinqProvider 
  223.      */
  224.     public function __construct($namePHPLinq_ILinqProvider $parentProvider null{
  225.         if (is_null(self::$_internalFunctions)) {
  226.             $internalFunctions get_defined_functions();
  227.             $internalFunctions['internal']['print'// Add as PHP function
  228.             self::$_internalFunctions $internalFunctions['internal'];
  229.         }
  230.         
  231.         $this->_from = $name;
  232.         
  233.         if (!is_null($parentProvider)) {
  234.             $this->_parentProvider = $parentProvider;
  235.             $parentProvider->addChildProvider($this);
  236.         }
  237.         
  238.         return $this;
  239.     }
  240.     
  241.     /**
  242.      * Class destructor
  243.      */
  244.     public function __destruct({
  245.         $this->_isDestructing = true;
  246.  
  247.         if (isset($this->_parentProvider&& !is_null($this->_parentProvider)) {
  248.             if (!$this->_parentProvider->__isDestructing()) {
  249.                 $this->_parentProvider->__destruct();
  250.             }
  251.             $this->_parentProvider = null;
  252.             unset($this->_parentProvider);
  253.         }
  254.         
  255.         if (!is_null($this->_childProviders)) {
  256.             foreach ($this->_childProviders as $provider{
  257.                 $provider->__destruct();
  258.                 $provider null;
  259.                 unset($provider);
  260.             }
  261.         }
  262.     }
  263.  
  264.     /**
  265.      * Is object destructing?
  266.      *
  267.      * @return bool 
  268.      */
  269.     public function __isDestructing({
  270.         return $this->_isDestructing;
  271.     }
  272.     
  273.     /**
  274.      * Get join condition
  275.      *
  276.      * @return PHPLinq_Expression 
  277.      */
  278.     public function getJoinCondition({
  279.         return $this->_joinCondition;
  280.     }
  281.     
  282.     /**
  283.      * Add child provider, used with joins
  284.      *
  285.      * @param PHPLinq_ILinqProvider $provider 
  286.      */
  287.     public function addChildProvider(PHPLinq_ILinqProvider $provider{
  288.         $this->_childProviders[$provider;
  289.     }
  290.     
  291.     /**
  292.      * Retrieve "from" name
  293.      *
  294.      * @return string 
  295.      */
  296.     public function getFromName({
  297.         return $this->_from;
  298.     }
  299.     
  300.     /**
  301.      * Retrieve data in data source
  302.      *
  303.      * @return mixed 
  304.      */
  305.     public function getSource({
  306.         return $this->_data;
  307.     }
  308.     
  309.     /**
  310.      * Set source of data
  311.      *
  312.      * @param mixed $source 
  313.      * @return PHPLinq_ILinqProvider 
  314.      */
  315.     public function in($source{
  316.         $this->_data = $source;
  317.         
  318.         // Configure PHPLinq_Adapter_Abstract
  319.         $zendDbAdapter     get_class($source->getAdapter());
  320.         $phpLinqAdapter str_replace('Zend_Db''PHPLinq'$zendDbAdapter);
  321.         require_once(str_replace('_''/'$phpLinqAdapter'.php');
  322.         $this->_adapter = new $phpLinqAdapter($source->getAdapter());
  323.         
  324.         return $this;
  325.     }
  326.     
  327.     /**
  328.      * Select
  329.      *
  330.      * @param  string    $expression    Expression which creates a resulting element
  331.      * @return mixed 
  332.      */
  333.     public function select($expression null{
  334.         // Return value
  335.         $returnValue array();
  336.         
  337.         // Zend_Db_Select expression
  338.         $zendDbSelect null;
  339.         
  340.         // Expression set?
  341.         if (is_null($expression|| $expression == ''{
  342.             $expression $this->_from . ' => ' $this->_from;
  343.         }
  344.         
  345.         // Data source
  346.         $dataSourceInfo $this->_data->info();
  347.         $dataSource $this->_data->select()->fromarray($this->_from => $dataSourceInfo['name'])$this->_columns );
  348.  
  349.         // Create selector expression
  350.         $selector new PHPLinq_Expression($expression$this->_from);
  351.         
  352.         // Build Zend_Db_Select chain
  353.         $zendDbSelect $dataSource;
  354.         
  355.         // Distinct set?
  356.         if (!is_null($this->_distinct)) {
  357.             $zendDbSelect $zendDbSelect->distinct();
  358.         }
  359.         
  360.         // Join set?
  361.         if (count($this->_childProviders0{
  362.             // Check if the child providers can be supported
  363.             foreach ($this->_childProviders as $provider{
  364.                 if (get_class($provider!= __CLASS__{
  365.                     throw new PHPLinq_Exception('Joins of ' get_class($provider' on ' . __CLASS__ . ' are not supported.');
  366.                 else {
  367.                     // Supported! Add join
  368.                     $joinInfo $provider->getSource()->info();
  369.                     $dataSource $dataSource->join(
  370.                         array($provider->getFromName(=> $joinInfo['name']),
  371.                         $this->_convertToSql($provider->getJoinCondition()->getFunction()->getFunctionCode())
  372.                     );
  373.                 }
  374.             }
  375.         }
  376.         
  377.         // Where expresion set? Evaluate it!
  378.         if (!is_null($this->_where)) {
  379.             $functionCode $this->_convertToSql(
  380.                 $this->_where->getFunction()->getFunctionCode(),
  381.                 $dataSource->getAdapter()->getQuoteIdentifierSymbol()
  382.             );
  383.             
  384.             $zendDbSelect $zendDbSelect->where($functionCode);
  385.         }
  386.         
  387.         // OrderBy set?
  388.         if (is_array($this->_orderBy&& count($this->_orderBy0{
  389.             $orderBy array();
  390.             foreach ($this->_orderBy as $orderByExpression{
  391.                 $orderBy[new Zend_Db_Expr(
  392.                     $this->_convertToSql(
  393.                         $orderByExpression->getExpression()->getFunction()->getFunctionCode()
  394.                     ($orderByExpression->isDescending(' DESC' ' ASC')
  395.                 );
  396.             }
  397.             $zendDbSelect $zendDbSelect->order($orderBy);
  398.         }
  399.         
  400.         // Take / skip
  401.         $zendDbSelect->limit($this->_take$this->_skip);
  402.         
  403.         // No integrity check needed, joins can be free format
  404.         $zendDbSelect->setIntegrityCheck(false);
  405.  
  406.         // Log query string
  407.         if (!is_null(self::$_queryCallback)) {
  408.             if (!is_array(self::$_queryCallback&& in_array(self::$_queryCallbackself::$_internalFunctions)) {
  409.                 self::$_queryCallback create_function('$query'self::$_queryCallback '($query);');
  410.             }
  411.             call_user_func(self::$_queryCallback$zendDbSelect->__toString());
  412.         }
  413.  
  414.         // Query
  415.         $results null;
  416.         if (count($this->_childProviders== 0{
  417.             // No join, easy!
  418.             $results $zendDbSelect->query(Zend_Db::FETCH_OBJ)->fetchAll();
  419.             
  420.             // Row class set? If so, convert all data!
  421.             if ($this->_data->getRowClass(!= 'Zend_Db_Table_Row'{
  422.                 $rowClass $this->_data->getRowClass();
  423.                 $tempResults array();
  424.                 
  425.                 foreach ($results as $result{
  426.                     $tempResult call_user_funcarray($rowClass'fromArray')(array)$result );
  427.                     $tempResults[$tempResult;
  428.                 }
  429.                 
  430.                 $results $tempResults;
  431.             }
  432.         else {
  433.             // Get data in mode FETCH_NAMED
  434.             $tempResults $zendDbSelect->query(Zend_Db::FETCH_NAMED)->fetchAll();
  435.             
  436.             // Fetch info
  437.             $info array$this->_data->info() );
  438.             foreach ($this->_childProviders as $provider{
  439.                 $info[$provider->getSource()->info();
  440.             }
  441.             
  442.             // Build proper resultset
  443.             $results         array();
  444.             $currentResult    null;
  445.             $currentObject    null;
  446.             foreach ($tempResults as $tempResult{
  447.                 $currentResult array();
  448.                 
  449.                 // For each table, add a new object with values
  450.                 for ($i 0$i count($info)$i++{
  451.                     // Fill object data
  452.                     $currentObject array();
  453.                     
  454.                     foreach($info[$i]['cols'as $column{
  455.                         if (!is_array($tempResult[$column])) {
  456.                             // Regular column
  457.                             $currentObject[$column$tempResult[$column];
  458.                         else {
  459.                             // Some searching to do...
  460.                             for ($j $i$j >= 0$j--{
  461.                                 if (isset($tempResult[$column][$j])) {
  462.                                     $currentObject[$column$tempResult[$column][$j];
  463.                                     $j = -1;
  464.                                 }
  465.                             }
  466.                         }
  467.                     }
  468.                     
  469.                     // Add object to result table.
  470.                     // Row class set? If so, convert all data!
  471.                     if ( ($i == && $this->_data->getRowClass(!= 'Zend_Db_Table_Row'||
  472.                             $this->_childProviders[$i 1]->getSource()->getRowClass(!= 'Zend_Db_Table_Row'{
  473.                         $rowClass $i == $this->_data->getRowClass($this->_childProviders[$i 1]->getSource()->getRowClass();
  474.                         $currentResult[call_user_funcarray($rowClass'fromArray')(array)$currentObject );
  475.                     else {
  476.                         $currentResult[= (object)$currentObject;
  477.                     }
  478.                 }
  479.                 
  480.                 $results[$currentResult;
  481.             }
  482.         }
  483.         
  484.         // Loop trough data source
  485.         foreach ($results as $value{
  486.             // Is it a valid element?
  487.             $isValid true;
  488.             
  489.             // OfType expresion set? Evaluate it!
  490.             if ($isValid && !is_null($this->_ofType)) {
  491.                 $isValid $this->_ofType->execute($value);
  492.             }
  493.                     
  494.             // The element is valid, check if it is our selection range
  495.             if ($isValid{
  496.                 // Skip element?
  497.                 if (!is_null($this->_skipWhile&& $this->_skipWhile->execute($value)) {
  498.                     $isValid false;
  499.                 }
  500.                 
  501.                 // Take element?
  502.                 if (!is_null($this->_takeWhile&& !$this->_takeWhile->execute($value)) {
  503.                     $isValid false;
  504.                     break;
  505.                 }
  506.  
  507.                 // Add the element to the return value if it is a valid element
  508.                 if ($isValid{
  509.                     $returnValue[$selector->execute($value);
  510.                 }
  511.             }
  512.         }
  513.         
  514.         // TODO:
  515.         // - join
  516.         // - aggregates 
  517.         // - translate more SQL!
  518.         
  519.         // Return value
  520.         return $returnValue;
  521.     }
  522.     
  523.     /**
  524.      * Converts PHP code into SQL code
  525.      *
  526.      * @param string $phpCode    Code to convert
  527.      * @return string 
  528.      */
  529.     private function _convertToSql($phpCode{
  530.         // Some fast cleaning
  531.         $phpCode str_replace('return '''$phpCode);
  532.         $phpCode str_replace(';'''$phpCode);
  533.         
  534.         // Adapter functions
  535.         $adapterFunctions     $this->_adapter->getFunctions();
  536.  
  537.         // Go parse!
  538.         $tokens         token_get_all('<?php ' $phpCode '?>');
  539.         $stack             array();
  540.         $tokenId        0;
  541.         $depth            0;
  542.  
  543.         for ($i 0$i count($tokens)$i++{
  544.             // Ignore token?
  545.             $ignoreToken    false;
  546.             
  547.             // Token details
  548.             $previousToken     $i $tokens[$i 1null;
  549.             $token             $tokens[$i];
  550.             $nextToken         $i count($tokens$tokens[$i 1null;
  551.             
  552.             // Parse token
  553.             if (is_array($token)) {
  554.                 switch ($token[0]{
  555.                     case T_OPEN_TAG:
  556.                     case T_CLOSE_TAG:
  557.                         $ignoreToken true;
  558.                         
  559.                         break;
  560.  
  561.                     case T_STRING:
  562.                         if (in_array($token[1]$adapterFunctions)) {
  563.                             // It is an adapter function!
  564.                             $stack[$tokenId]['token''$this->_adapter->' $token[1];
  565.                             $stack[$tokenId]['type']  self::T_FUNCTION;
  566.                         else if (in_array($token[1]self::$_internalFunctions)) {
  567.                             // If it is a PHP function, let's take a lucky
  568.                             // shot and expect the syntax to be the same...
  569.                             $stack[$tokenId]['token'$token[1];
  570.                             $stack[$tokenId]['type']  self::T_FUNCTION;
  571.                         else {
  572.                             // Probably some sort of constant / property name
  573.                             if (!is_null($previousToken&& is_array($previousToken&& $previousToken[0== T_OBJECT_OPERATOR{
  574.                                 $stack[$tokenId]['token'$this->_adapter->quoteIdentifier($token[1]'\'';
  575.                                 $stack[$tokenId]['type']  self::T_PROPERTY;
  576.                             else {
  577.                                 $stack[$tokenId]['token''\'' $token[1'\'';
  578.                                 $stack[$tokenId]['type']  self::T_CONSTANT;
  579.                             }
  580.                         }
  581.                         
  582.                         break;
  583.                         
  584.                     case T_VARIABLE:
  585.                         $stack[$tokenId]['token'$this->_adapter->quote($token[1]);
  586.                         $stack[$tokenId]['type']  self::T_VARIABLE;
  587.                         
  588.                         break;
  589.                         
  590.                     case T_OBJECT_OPERATOR
  591.                         if (!is_null($previousToken&& is_array($previousToken&& $previousToken[0== T_VARIABLE{
  592.                             $stack[$tokenId 1]['token''\'' addslashes($stack[$tokenId 1]['token']'.';
  593.                             
  594.                             $ignoreToken true;
  595.                         else {
  596.                             $stack[$tokenId]['token''\'.\'';
  597.                         }
  598.                         $stack[$tokenId]['type']  self::T_OBJECT_OPERATOR;
  599.                         
  600.                         break;
  601.                     
  602.                     case T_IS_IDENTICAL:
  603.                     case T_IS_EQUAL:
  604.                         $stack[$tokenId]['token''\' ' $this->_adapter->operator('='' \'';
  605.                         $stack[$tokenId]['type']  self::T_OPERATOR;
  606.                         
  607.                         break;
  608.                         
  609.                     case T_IS_NOT_EQUAL:
  610.                     case T_IS_NOT_IDENTICAL:
  611.                         $stack[$tokenId]['token''\' ' $this->_adapter->operator('!='' \'';
  612.                         $stack[$tokenId]['type']  self::T_OPERATOR;
  613.                         
  614.                         break;
  615.                         
  616.                     case T_IS_GREATER_OR_EQUAL:
  617.                         $stack[$tokenId]['token''\' ' $this->_adapter->operator('>='' \'';
  618.                         $stack[$tokenId]['type']  self::T_OPERATOR;
  619.  
  620.                         break;
  621.  
  622.                     case T_IS_SMALLER_OR_EQUAL:
  623.                         $stack[$tokenId]['token''\' ' $this->_adapter->operator('<='' \'';
  624.                         $stack[$tokenId]['type']  self::T_OPERATOR;
  625.  
  626.                         break;
  627.                     
  628.                     case T_BOOLEAN_AND:
  629.                     case T_LOGICAL_AND:
  630.                         $stack[$tokenId]['token''\' ' $this->_adapter->operand('AND'' \''
  631.                         $stack[$tokenId]['type']  self::T_OPERAND;
  632.                         
  633.                         break;
  634.                         
  635.                     case T_BOOLEAN_OR:    
  636.                     case T_LOGICAL_OR:
  637.                         $stack[$tokenId]['token''\' ' $this->_adapter->operand('OR'' \'';
  638.                         $stack[$tokenId]['type']  self::T_OPERAND;
  639.  
  640.                         break;
  641.  
  642.                     default
  643.                         $stack[$tokenId]['token''\'' $token[1'\'';
  644.                         $stack[$tokenId]['type']  self::T_DEFAULT;
  645.  
  646.                         break;
  647.                 }
  648.             else {
  649.                 // Simple token
  650.                 if ($token != '(' && $token != ')'{
  651.                     $stack[$tokenId]['token'$token;
  652.                     $stack[$tokenId]['type']  self::T_SIMPLE;
  653.                     
  654.                     if ($token == ','{
  655.                         $stack[$tokenId]['type']  self::T_ARGUMENT;
  656.                     else if ($token == '+' || $token == '-' || $token == '*' || $token == '/' || $token == '%' || $token == '.'{
  657.                         $stack[$tokenId]['token''\'' $this->_adapter->operator($token'\'';
  658.                         $stack[$tokenId]['type']  self::T_ARITHMETIC;
  659.                     else if ($token == '>' || $token == '<'{
  660.                         $stack[$tokenId]['token''\' ' $this->_adapter->operator($token' \'';
  661.                         $stack[$tokenId]['type']  self::T_OPERATOR;
  662.                     }
  663.                 else {
  664.                     $stack[$tokenId]['token''';
  665.                     $stack[$tokenId]['type']  self::T_START_STOP;
  666.                 }
  667.             }
  668.  
  669.             // Token metadata
  670.             if (!$ignoreToken{
  671.                 // Depth
  672.                 if (!is_array($token&& $token == '('$depth++;
  673.                 if (!is_array($token&& $token == ')'$depth--;    
  674.                 $stack[$tokenId]['depth'$depth;         
  675.                 
  676.                 // Identifier
  677.                 $tokenId++;
  678.             }
  679.         }
  680.         
  681.         // Display tree
  682.         /*
  683.         foreach ($stack as $node) {
  684.             for ($i = 0; $i <= $node['depth']; $i++) {
  685.                 echo "| ";
  686.             }
  687.             
  688.             echo $node['type'] . ' - ' . $node['token'] . "\r\n";
  689.         }
  690.         die();
  691.         */
  692.  
  693.         // Build compilation string
  694.         $compileCode     '';
  695.         $functionDepth    array();
  696.         $depth             0;
  697.         for ($i 0$i count($stack)$i++{
  698.             // Token details
  699.             $previousToken     $i $stack[$i 1null;
  700.             $token             $stack[$i];
  701.             $nextToken         $i count($stack$stack[$i 1null;  
  702.             
  703.             // Regular token
  704.             if ($token['depth'== $depth && $token['type'!= self::T_START_STOP{
  705.                 $compileCode .= $token['token'];
  706.             }
  707.             
  708.             // Start/stop
  709.             if ($token['depth'$depth && $token['type'== self::T_START_STOP && !is_null($previousToken&& $previousToken['type'== self::T_FUNCTION{
  710.                 $compileCode .= '(';
  711.                 $functionDepth[$depthself::T_FUNCTION;
  712.             else if ($token['depth'$depth && $token['type'== self::T_START_STOP && $functionDepth$token['depth'] ] == self::T_FUNCTION{
  713.                 $compileCode .= ')';
  714.             else if ($token['depth'$depth && $token['type'== self::T_START_STOP{
  715.                 $compileCode .= '\'(\'';
  716.                 $functionDepth[$depthself::T_START_STOP;
  717.             else if ($token['depth'$depth && $token['type'== self::T_START_STOP{
  718.                 $compileCode .= '\')\'';
  719.             }
  720.             
  721.             // Next token needs concatenation?
  722.             if (!is_null($nextToken&& $nextToken['type'!= self::T_ARGUMENT{
  723.                 if (
  724.                     !($token['type'== self::T_FUNCTION && $nextToken['type'== self::T_START_STOP&&
  725.                     !(!is_null($previousToken&& $previousToken['type'== self::T_FUNCTION && $token['type'== self::T_START_STOP&&
  726.                     
  727.                     !($token['type'== self::T_ARGUMENT&&
  728.                     
  729.                     !(!is_null($nextToken&& $nextToken['type'== self::T_START_STOP && isset($functionDepth$nextToken['depth'] ]&& $functionDepth$nextToken['depth'] ] == self::T_FUNCTION&&
  730.                     
  731.                     !($token['type'== self::T_VARIABLE && !is_null($nextToken&& $nextToken['type'== self::T_PROPERTY)
  732.                     {
  733.                     $compileCode .= ' . ';
  734.                 }
  735.             }
  736.  
  737.             // Depth
  738.             if ($token['depth'$depth{
  739.                 unset($functionDepth[$token['depth']]);
  740.             }
  741.             $depth $token['depth'];
  742.         }
  743.  
  744.         // Compile
  745.         $compileCode '$compileCode = ' $compileCode ';';
  746.         eval($compileCode);
  747.  
  748.         return $compileCode;
  749.     }
  750.     
  751.     /**
  752.      * Where
  753.      *
  754.      * @param  string    $expression    Expression checking if an element should be contained
  755.      * @return PHPLinq_ILinqProvider 
  756.      */
  757.     public function where($expression{
  758.         $this->_where = !is_null($expressionnew PHPLinq_Expression($expression$this->_fromnull;
  759.         return $this;
  760.     }
  761.     
  762.     /**
  763.      * Take $n elements
  764.      *
  765.      * @param int $n 
  766.      * @return PHPLinq_ILinqProvider 
  767.      */
  768.     public function take($n{
  769.         $this->_take = $n;
  770.         return $this;
  771.     }
  772.     
  773.     /**
  774.      * Skip $n elements
  775.      *
  776.      * @param int $n 
  777.      * @return PHPLinq_ILinqProvider 
  778.      */
  779.     public function skip($n{
  780.         $this->_skip = $n;
  781.         return $this;
  782.     }
  783.     
  784.     /**
  785.      * Take elements while $expression evaluates to true
  786.      *
  787.      * @param  string    $expression    Expression to evaluate
  788.      * @return PHPLinq_ILinqProvider 
  789.      */
  790.     public function takeWhile($expression{
  791.         $this->_takeWhile = !is_null($expressionnew PHPLinq_Expression($expression$this->_fromnull;
  792.         return $this;
  793.     }
  794.     
  795.     /**
  796.      * Skip elements while $expression evaluates to true
  797.      *
  798.      * @param  string    $expression    Expression to evaluate
  799.      * @return PHPLinq_ILinqProvider 
  800.      */
  801.     public function skipWhile($expression{
  802.         $this->_skipWhile = !is_null($expressionnew PHPLinq_Expression($expression$this->_fromnull;
  803.         return $this;
  804.     }
  805.     
  806.     /**
  807.      * OrderBy
  808.      *
  809.      * @param  string    $expression    Expression to order elements by
  810.      * @param  string    $comparer    Comparer function (taking 2 arguments, returning -1, 0, 1)
  811.      * @return PHPLinq_ILinqProvider 
  812.      */
  813.     public function orderBy($expression$comparer null{
  814.         $this->_orderBy[0new PHPLinq_OrderByExpression($expression$this->_fromfalse$comparer);
  815.         return $this;
  816.     }
  817.     
  818.     /**
  819.      * OrderByDescending
  820.      *
  821.      * @param  string    $expression    Expression to order elements by
  822.      * @param  string    $comparer    Comparer function (taking 2 arguments, returning -1, 0, 1)
  823.      * @return PHPLinq_ILinqProvider 
  824.      */
  825.     public function orderByDescending($expression$comparer null{
  826.         $this->_orderBy[0new PHPLinq_OrderByExpression($expression$this->_fromtrue$comparer);
  827.         return $this;
  828.     }
  829.     
  830.     /**
  831.      * ThenBy
  832.      *
  833.      * @param  string    $expression    Expression to order elements by
  834.      * @param  string    $comparer    Comparer function (taking 2 arguments, returning -1, 0, 1)
  835.      * @return PHPLinq_ILinqProvider 
  836.      */
  837.     public function thenBy($expression$comparer null{
  838.         $this->_orderBy[new PHPLinq_OrderByExpression($expression$this->_fromfalse$comparer);
  839.         return $this;
  840.     }
  841.     
  842.     /**
  843.      * ThenByDescending
  844.      *
  845.      * @param  string    $expression    Expression to order elements by
  846.      * @param  string    $comparer    Comparer function (taking 2 arguments, returning -1, 0, 1)
  847.      * @return PHPLinq_ILinqProvider 
  848.      */
  849.     public function thenByDescending($expression$comparer null{
  850.         $this->_orderBy[new PHPLinq_OrderByExpression($expression$this->_fromtrue$comparer);
  851.         return $this;
  852.     }
  853.     
  854.     /**
  855.      * Distinct
  856.      *
  857.      * @param  string    $expression    Ignored.
  858.      * @return PHPLinq_ILinqProvider 
  859.      */
  860.     public function distinct($expression{
  861.         $this->_distinct = !is_null($expressionnew PHPLinq_Expression($expression$this->_fromnull;
  862.         return $this;
  863.     }
  864.     
  865.     /**
  866.      * Select the elements of a certain type
  867.      *
  868.      * @param string $type    Type name
  869.      */
  870.     public function ofType($type{
  871.         // Create a new expression
  872.         $expression $this->_from . ' => ';
  873.         
  874.         // Evaluate type
  875.         switch (strtolower($type)) {
  876.             case 'array':
  877.             case 'bool':
  878.             case 'double':
  879.             case 'float':
  880.             case 'int':
  881.             case 'integer':
  882.             case 'long':
  883.             case 'null':
  884.             case 'numeric':
  885.             case 'object':
  886.             case 'real':
  887.             case 'scalar':
  888.             case 'string':
  889.                 $expression .= 'is_' strtolower($type'(' $this->_from . ')';
  890.                 break;
  891.             default:
  892.                 $expression .= 'is_a(' $this->_from . ', "' $type '")';
  893.                 break;
  894.         }
  895.         
  896.         // Assign expression
  897.         $this->_ofType = new PHPLinq_Expression($expression$this->_from);
  898.         return $this;
  899.     }
  900.     
  901.     /**
  902.      * Any
  903.      *
  904.      * @param  string    $expression    Expression checking if an element is contained
  905.      * @return boolean 
  906.      */
  907.     public function any($expression{
  908.         $originalWhere $this->_where;
  909.         
  910.         $this->_columns = 'COUNT(*) AS cnt';
  911.         $countFrom $this->_from . ' => ' $this->_from . '->cnt';
  912.         
  913.         $result $this->where($expression)->select($countFrom);
  914.         
  915.         $this->_columns = '*';
  916.         
  917.         $this->_where = $originalWhere;
  918.         
  919.         return $result[00;
  920.     }
  921.     
  922.     /**
  923.      * All
  924.      *
  925.      * @param  string    $expression    Expression checking if an all elements are contained
  926.      * @return boolean 
  927.      */
  928.     public function all($expression{
  929.         $originalWhere $this->_where;
  930.         
  931.         $this->_columns = 'COUNT(*) AS cnt';
  932.         $countFrom $this->_from . ' => ' $this->_from . '->cnt';
  933.         
  934.         $result1 $this->where($expression)->select($countFrom);
  935.         $result2 $this->where(null)->select($countFrom);
  936.  
  937.         $this->_columns = '*';
  938.         
  939.         $this->_where = $originalWhere;
  940.         
  941.         return $result1[0== $result2[0];
  942.     }
  943.  
  944.     /**
  945.      * Contains - Not performed as query! (heavy)
  946.      *
  947.      * @param mixed $element Is the $element contained?
  948.      * @return boolean 
  949.      */
  950.     public function contains($element{
  951.         return in_array($element$this->select());
  952.     }
  953.     
  954.     /**
  955.      * Reverse elements - Not performed as query! (heavy)
  956.      * 
  957.      * @param bool $preserveKeys Preserve keys?
  958.      * @return PHPLinq_ILinqProvider 
  959.      */
  960.     public function reverse($preserveKeys null{
  961.         $data array_reverse($this->select()$preserveKeys);
  962.         return linqfrom($this->_from)->in($data);
  963.     }
  964.     
  965.     /**
  966.      * Element at index
  967.      *
  968.      * @param mixed $index Index
  969.      * @return mixed Element at $index
  970.      */
  971.     public function elementAt($index null{
  972.         $originalWhere $this->_where;
  973.         
  974.         $result $this->where(null)->take(1)->skip($index)->select();
  975.         
  976.         $this->_where = $originalWhere;
  977.         
  978.         if (count($result0{
  979.             return array_shift($result);
  980.         }
  981.         return null;
  982.     }
  983.     
  984.     /**
  985.      * Element at index or default
  986.      *
  987.      * @param mixed $index Index
  988.      * @param  mixed $defaultValue Default value to return if nothing is found
  989.      * @return mixed Element at $index
  990.      */
  991.     public function elementAtOrDefault($index null$defaultValue null{
  992.         $returnValue $this->elementAt($index);
  993.         if (!is_null($returnValue)) {
  994.             return $returnValue;
  995.         else {
  996.             return $defaultValue;
  997.         }
  998.     }
  999.     
  1000.     /**
  1001.      * Concatenate data
  1002.      *
  1003.      * @param mixed $source 
  1004.      * @return PHPLinq_ILinqProvider 
  1005.      */
  1006.     public function concat($source{
  1007.         $data array_merge($this->select()$source);
  1008.         return linqfrom($this->_from)->in($data);
  1009.     }
  1010.     
  1011.     /**
  1012.      * First
  1013.      *
  1014.      * @param  string    $expression    Expression which creates a resulting element
  1015.      * @return mixed 
  1016.      */
  1017.     public function first($expression null{
  1018.         $linqCommand clone $this;
  1019.         $result $linqCommand->skip(0)->take(1)->select($expression);
  1020.         if (count($result0{
  1021.             return array_shift($result);
  1022.         }
  1023.         return null;
  1024.     }
  1025.     
  1026.     /**
  1027.      * FirstOrDefault
  1028.      *
  1029.      * @param  string    $expression    Expression which creates a resulting element
  1030.      * @param  mixed    $defaultValue Default value to return if nothing is found
  1031.      * @return mixed 
  1032.      */
  1033.     public function firstOrDefault ($expression null$defaultValue null{
  1034.         $returnValue $this->first($expression);
  1035.         if (!is_null($returnValue)) {
  1036.             return $returnValue;
  1037.         else {
  1038.             return $defaultValue;
  1039.         }
  1040.     }
  1041.     
  1042.     /**
  1043.      * Last
  1044.      *
  1045.      * @param  string    $expression    Expression which creates a resulting element
  1046.      * @return mixed 
  1047.      */
  1048.     public function last($expression null{
  1049.         $linqCommand clone $this;
  1050.         $result $linqCommand->reverse()->skip(0)->take(1)->select($expression);
  1051.         if (count($result0{
  1052.             return array_shift($result);
  1053.         }
  1054.         return null;
  1055.     }
  1056.     
  1057.     /**
  1058.      * LastOrDefault
  1059.      *
  1060.      * @param  string    $expression    Expression which creates a resulting element
  1061.      * @param  mixed    $defaultValue Default value to return if nothing is found
  1062.      * @return mixed 
  1063.      */
  1064.     public function lastOrDefault ($expression null$defaultValue null{
  1065.         $returnValue $this->last($expression);
  1066.         if (!is_null($returnValue)) {
  1067.             return $returnValue;
  1068.         else {
  1069.             return $defaultValue;
  1070.         }
  1071.     }
  1072.     
  1073.     /**
  1074.      * Single
  1075.      *
  1076.      * @param  string    $expression    Expression which creates a resulting element
  1077.      * @return mixed 
  1078.      */
  1079.     public function single($expression null{
  1080.         return $this->first($expression);
  1081.     }
  1082.     
  1083.     /**
  1084.      * SingleOrDefault
  1085.      *
  1086.      * @param  string    $expression    Expression which creates a resulting element
  1087.      * @param  mixed    $defaultValue Default value to return if nothing is found
  1088.      * @return mixed 
  1089.      */
  1090.     public function singleOrDefault ($expression null$defaultValue null{
  1091.         return $this->firstOrDefault($expression$defaultValue);
  1092.     }
  1093.     
  1094.     /**
  1095.      * Join
  1096.      *
  1097.      * @param string $name 
  1098.      * @return PHPLinq_Initiator 
  1099.      */
  1100.     public function join($name{
  1101.         return new PHPLinq_Initiator($name$this);
  1102.     }
  1103.     
  1104.     /**
  1105.      * On
  1106.      *
  1107.      * @param  string    $expression    Expression representing join condition
  1108.      * @return PHPLinq_ILinqProvider 
  1109.      */
  1110.     public function on($expression{
  1111.         $this->_joinCondition = new PHPLinq_Expression($expression$this->_from);
  1112.         return $this->_parentProvider;
  1113.     }
  1114.     
  1115.     /**
  1116.      * Count elements
  1117.      *
  1118.      * @return int Element count
  1119.      */
  1120.     public function count({
  1121.         return count($this->_data);
  1122.     }
  1123.     
  1124.     /**
  1125.      * Sum elements
  1126.      *
  1127.      * @return mixed Sum of elements
  1128.      */
  1129.     public function sum({
  1130.         return array_sum($this->_data)// $this->aggregate(0, '$s, $t => $s + $t');
  1131.     }
  1132.     
  1133.     /**
  1134.      * Minimum of elements
  1135.      *
  1136.      * @return mixed Minimum of elements
  1137.      */
  1138.     public function min(){
  1139.         return min($this->_data);
  1140.     }
  1141.     
  1142.     /**
  1143.      * Maximum of elements
  1144.      *
  1145.      * @return mixed Maximum of elements
  1146.      */
  1147.     public function max(){
  1148.         return max($this->_data);
  1149.     }
  1150.     
  1151.     /**
  1152.      * Average of elements
  1153.      *
  1154.      * @return mixed Average of elements
  1155.      */
  1156.     public function average(){
  1157.         return $this->sum($this->count();
  1158.     }
  1159.  
  1160.     /**
  1161.      * Aggregate
  1162.      * 
  1163.      * Example: Equivalent of count(): $this->aggregate(0, '$s, $t => $s + 1');
  1164.      *
  1165.      * @param int $seed    Seed
  1166.      * @param string $expression    Expression defining the aggregate
  1167.      * @return mixed aggregate
  1168.      */
  1169.     public function aggregate($seed 0$expression{
  1170.         $codeExpression new PHPLinq_Expression($expression);
  1171.         
  1172.         $runningValue $seed;
  1173.         foreach ($this->_data as $value{
  1174.             $runningValue $codeExpression->executearray($runningValue$value) );
  1175.         }
  1176.         
  1177.         return $runningValue;
  1178.     }
  1179.     
  1180.     /**
  1181.      * Get query callback (static for all PHPLinq_LinqToZendDb !)
  1182.      * 
  1183.      * Function accepting a string to which query strings can be logged.
  1184.      *
  1185.      * @return mixed 
  1186.      */
  1187.     public static function getQueryCallback({
  1188.         return self::$_queryCallback;
  1189.     }
  1190.     
  1191.     /**
  1192.      * Set query callback (static for all PHPLinq_LinqToZendDb !)
  1193.      * 
  1194.      * Function accepting a string to which query strings can be logged.
  1195.      *
  1196.      * @param mixed $value 
  1197.      */
  1198.     public static function setQueryCallback($value null{
  1199.         self::$_queryCallback $value;
  1200.     }
  1201. }

Documentation generated on Tue, 27 Jan 2009 08:29:31 +0100 by phpDocumentor 1.4.1