source: trunk/prototype/api/newcontroller.php @ 6528

Revision 6528, 19.2 KB checked in by gustavo, 12 years ago (diff)

Ticket #2766 - Merge do branch das novas funcionalidaes para o trunk

Line 
1<?php
2
3if( !defined( 'ROOTPATH' ) )
4    define( 'ROOTPATH', dirname(__FILE__).'/..' );
5
6require_once(ROOTPATH.'/api/config.php');
7use prototype\api\Config as Config;
8
9/**
10TODO list:
11
12  * definir de forma centralizada os caminhos e as constantes necessárias;
13  * criar um User Agent detect e um OS server detect para customizações espeçíficas de cada browser / servidor;
14  * criar um registrador para fallback handlers;
15  * criar um dependency manager na configuração dos serviços, para poder gerenciar os imports corretamente
16  * criar um login e a recuperação da sessão;
17
18*/
19
20class Controller {
21
22        static $cache;
23        static $services = array();
24        static $interceptors = array();
25        static $config = array();
26        static $includes = array();
27        static $tx = array();
28        static $txID = 0;
29        static $wallet;
30
31        public function __destruct()
32        {
33//          if( $this->service )
34//              $this->service->close();
35//          else
36            self::closeAll();
37        }
38
39        public static function closeAll()
40        {
41            if( self::$services )
42                foreach( self::$services as $serviceName => $service )
43                    if( self::$config[ $serviceName ]['type'] === 'service' )
44                      $service->close();
45        }
46
47        public static function clearAll()
48            {
49            return self::$cache->clearAll();
50            }
51
52        public static function clear( $id )
53        {
54            return self::$cache->clear( $id );
55        }
56
57        public static function check( $id )
58        {
59            return self::$cache->get( $id );
60        }
61
62        public static function store( $id, $data, $expires, $compressed )
63        {
64            return self::$cache->put( $id, $data, $expires, $compressed );
65        }
66
67       
68
69//      public static function read( $concept, $id = false, $options = false )
70//      {
71//          if( !isset($URI['id']) || !$URI['id'] )
72//              return self::find( $URI, $params, $criteria );
73//
74//          return self::call( 'read', $URI, $params, $criteria );
75//      }
76
77//      public static function deleteAll( $URI, $params = false, $criteria = false )
78//      {
79//          if( isset($URI['id']) && $URI['id'] )
80//              return self::delete( $URI, $params, $criteria );
81//
82//          return self::call( 'deleteAll', $URI, $params, $criteria );
83//      }
84
85        //      public static function replace( $URI, $params, $criteria = false )
86//      {
87//          if( isset($URI['id']) && $URI['id'] )
88//              return self::update( $URI, $params, $criteria );
89//
90//          return self::call( 'replace', $URI, $params, $criteria );
91//      }
92
93        public static function find( $concept, $options )
94        {   
95            return self::get( $options['filter'],  self::context( $options, array( 'concept' => $concept ) ) );
96        }
97
98        public static function delete( $concept, $id = false, $options = array() )
99        {
100            return self::put( false, self::context( $options, array( 'concept' => $concept, 'id' => $id ) ) );
101        }
102
103        public static function update( $concept, $data, $id = false, $options = array() )
104        {
105            return self::put( $data, self::context( $options, array( 'concept' => $concept, 'id' => $id ) ) );
106        }
107
108        public static function create( $concept, $data, $options = array() )
109        {
110            return self::put( $data, self::context( $options, array( 'concept' => $concept ) ) );
111        }
112
113        public static function put( $data, $options )
114        {
115            try
116            {
117                $context = self::context( $options );
118
119                $txId = self::begin( $context['service'] );
120
121                if( $context['format'] )
122                    $data = self::parse( $data, $context['format'], $options );
123
124                if( !isset($options['concept']) )
125                {
126                    $return = array();
127
128                    for( $data as $concept => $dt )
129                         $return[] = self::put( $dt, array_merge( array( 'concept' => $concept ), $options ) );
130                       
131                    return $return;
132                }
133
134
135                $model = self::$models[ $options['concept'] ];
136
137                $postpone = array();
138
139                if( $data )
140                {
141                    foreach( $model['hasMany'] as $linkName => $linkTarget )
142                    {
143                        $postpone[$linkTarget] = $dt[$linkName];
144                    }
145                    foreach( $model['hasOne'] as $linkName => $linkTarget )
146                    {
147                        if( isset( $dt[$linkName] ) && is_array( $dt[$linkName] ) )
148                            $dt[$linkName] = self::put( $dt[$linkName],  array_merge( array( 'concept' => $linkTarget ), $options ) );
149                    }
150                }
151
152                $method =        $dt ? isset( $dt['id'] ) ?
153                                'update' : 'create' : 'delete';
154
155                $context['id'] = $dt ? isset( $dt['id'] ) ?
156                                 $dt['id'] : false : $context['id'];
157
158                self::before( $concept.':'.$method, &$context, $options );
159                self::call( $method, $options, $dt );
160                self::after( $concept.':'.$method, &$context, $options );
161
162                $result = $context['result'];
163
164                if( !is_bool( $result ) && !is_string( $result ) && isset( $result['id'] ) )
165                      $context['id'] = $result['id'];
166
167                foreach( $postpone as $linkTarget => $dt )
168                      foreach( $dt as $ii => $value )
169                      {
170                          if( !is_array( $value ) )
171                            $value = array( 'id' => $value );
172
173                          $value[ $options['concept'] ] = $options['id'];
174
175                          self::put( $value, array_merge( array( 'concept' => $linkTarget ), $options ) );
176                      }
177
178                if( $txId )
179                    return self::commit( $options['service'], $txId );
180            }
181            catch( Exception $e )
182            {
183                if( !self::fallback( $e ) )
184                    self::closeAll();
185
186                return( false );
187            }
188       
189            return( $options['id'] );
190        }
191
192        public static function context( $options, $custom = false )
193        {
194            if( $service )
195                $options['service'] = $service;
196
197            return $options;
198        }
199
200        public static function get( $filter, $options = false )
201        {
202            return self::call( 'find', self::context( $options, array( 'filter' => $filter ) ) );
203        }
204
205        public static function connect( $service, $options )
206        {
207            $result = self::call( 'open', self::context( $options, array( 'service' => $service ) ) );
208
209            if( is_string( $result ) )
210                throw new Exception( $result );
211
212            return( true );
213        }
214
215        public static function begin( $service, $txId = false, $options = false )
216        {
217            $context = self::context( $options );
218
219            $result = self::call( 'begin', $options );
220
221            if( !$txId )
222                $txId = $result ? $result : self::$txID++;
223
224            if( isset( self::$transactions[ $txId ] ) )
225                return( false );
226
227            self::$transactions[ $txId ] = array( 'txID' => $txId );
228            self::$transactions[ $service ][] =& self::$transactions[ $txId ];
229
230            return( $txId );
231        }
232
233        public static function commit( $service, $txId = false, $options = false )
234        {
235            $context = self::context( $options );
236
237            $txs = self::$transactions[ $service . ( $txId ? '.'.$txId : '' ) ];
238
239            if( !is_array( $txs ) ) $txs = array( $txs );
240
241            $return = array();
242
243            foreach( $txs as $tx )
244            {
245                $txID = $tx['txID'];
246
247                $result = false;
248
249                if( !$options || !$options['rollback'] )
250                    $result = self::call( 'commit', $context, $tx );
251
252                if( !$result )
253                    $result = self::call( 'rollback', $context, $tx );
254
255                $return[ $txID ] = $result;
256
257                unset( self::$transactions[ $txID ] );
258            }
259
260            return( $txId ? $return[ $txId ] : $return );
261        }
262
263        public static function rollback( $service, $txId = false, $options = array() )
264        {
265            return self::commit( $service, $txId, self::context( $options, array( 'rollback' => true ) ) );
266        }
267
268        public static function fallback( $exception, $context ) // ver a melhor forma de tratar exceptions nesse caso
269        {
270            if( !self::emmit( 'fallback', self::context( $context, array( 'exception' => $exception ) ), $exception ) )
271                error_log( $exception->getMessage() );
272 
273            return( true );
274        }
275
276        public static function format( $data, $service = false, $options = array() )
277        {
278            return self::call( 'format', self::context( $options, array( 'service' => $service ) ), $data );
279        }
280
281        public static function parse( $data, $service = false, $options = array() )
282        {
283            return self::call( 'parse', self::context( $options, array( 'service' => $service ) ), $data );
284        }
285
286        public static function before( $eventName, &$context, $extra = false )
287        {
288            return self::emmit( 'before.'.$eventName, $context, $extra );
289        }
290
291        public static function after( $eventName, &$context, $extra = false )
292        {
293            return self::emmit( 'after.'.$eventName, $context, $extra );
294        }
295
296        public static function emmit( $eventName, &$context, $extra = false )
297        {
298            if( self::$listeners[ $eventName ] )
299                return( false );
300
301            foreach( self::$listeners[ $eventName ] as $listen => $listener )
302            {
303                 $return = $listener->$listen( $context, $extra );
304
305                 if( $return === false )
306                    return( true );
307
308                 $context = self::context( $context, array( 'return' => $return ) );
309            }
310
311            return( $return );
312        }
313
314        //TODO: Compatibilizar as configs relativas aos modulos, adicionando os mesmos nas options passadas
315        public static function call( $method, $options, $data = false ) //see how data fit in it
316        {
317            try
318            {
319                $context = self::context( $options, array( 'data' => $data ) );
320
321                $service = $context['service'];
322
323                if( $context['config'] )
324                    self::connect( $service, $context['config'] );
325
326                self::before( $service.'.'.$method, &$context, $options );
327
328                if( self::$services[ $service ] )
329                    switch( $method )
330                    {
331                        case 'find': $return = self::$services[ $service ]->find( $context['URI'], $context['properties'], $context['criteria'] ); break;
332
333                        case 'read': $return = self::$services[ $service ]->read( $context['URI'], $context['properties']/*, $criteria*/ ); break;
334
335                        case 'create': $return = self::$services[ $service ]->create( $context['URI'], $context['properties']/*, $criteria*/ ); break;
336
337                        case 'delete': $return = self::$services[ $service ]->delete( $context['URI'], $context['properties']/*, $criteria*/ ); break;
338
339                        case 'deleteAll': $return = self::$services[ $service ]->deleteAll( $context['URI'], $context['properties'], $context['criteria'] ); break;
340
341                        case 'update': $return = self::$services[ $service ]->update( $context['URI'], $context['properties']/*, $criteria*/ ); break;
342
343                        case 'replace': $return = self::$services[ $service ]->replace( $context['URI'], $context['properties'], $context['criteria'] ); break;
344
345                        case 'begin': $return = self::$services[ $service ]->begin( $context['URI'] ); break;
346
347                        case 'commit': $return = self::$services[ $service ]->commit( $context['URI'], $context['criteria'] ); break;
348
349                        case 'rollback': $return = self::$services[ $service ]->rollback( $context['URI'], $context['criteria'] ); break;
350
351                        case 'parse': $return = self::$services[ $service ]->parse( $context['properties'], $context['criteria'] ); break;
352
353                        case 'analize': $return = self::$services[ $service ]->analize( $context['properties'], $context['criteria'] ); break;
354
355                        case 'format': $return = self::$services[ $service ]->format( $context['properties'], $context['criteria'] ); break;
356
357                        default : $return = self::$services[ $service ]->$method( $context['properties'], $context['criteria'] );
358                    }
359
360                $context['return'] = $return;
361
362                self::after( $service.'.'.$method, &$context, $options );
363            }
364            catch( Exception $e )
365            {
366                if( !self::fallback( $e ) )
367                    self::closeAll();
368
369                return( false );
370            }
371
372            return( $context['return'] );
373        }
374
375//      public static function URI( $className, $id = false, $service = false )
376//      {
377//          return array( 'concept' => $className,
378//                        'service' => $service ? $service : false,
379//                        'id' => $id ? $id : '' );
380//      }
381
382        //TODO: Compatibilizar as configs relativas aos modulos, adicionando os mesmo nos parametros passados
383//      public static function links( $concept = false )
384//      {
385//          if( !isset(self::$config[ $concept ]) )
386//            self::$config[ $concept ] = self::loadConfig( $concept );
387//
388//          return( isset(self::$config[ $concept ]['links']) ?
389//                        self::$config[ $concept ]['links'] : array() );
390//      }
391
392//      public static function isConcept( $concept )
393//      {
394//          if( isset( self::$config[ $concept ] ) &&
395//              self::$config[ $concept ] )
396//              return( true );
397//              else
398//              return file_exists( ROOTPATH."/config/$concept.ini" );
399//      }
400
401//      public static function getConcept( $concept, $moduleName = false )
402//      {
403//          if( isset( self::$config[ $concept ] ) )
404//              return( self::$config[ $concept ] );
405//
406//          return( self::$config[ $concept ] = self::loadConfig( $concept, $moduleName ) );
407//      }
408
409        public static function loadCache( $cacheType = 'Memory' )
410        {
411            include_once( "cache/MemoryCache.php" );
412            return new MemoryCache();
413        }
414
415        //TODO: Compatibilizar as configs relativas aos modulos, adicionando os mesmo nos parametros passados
416        public static function loadConfig( $className, $isService = false)
417        {
418            $fileName = $className.'.'.($isService ? 'srv' : 'ini');
419
420            $config = self::$cache->get( $fileName );
421       
422            if( !$config )
423            {
424                $config = parse_ini_file( ROOTPATH.'/config/'.$fileName, true );
425
426                self::$cache->put( $fileName, $config );
427            }
428
429            return( $config );
430        }
431
432        public static function import( $path, $ext = ".php" )
433        {
434            if( !isset(self::$includes[$path]) )
435        {
436                require_once( ROOTPATH.'/'.$path.$ext );
437                self::$includes[$path] = false;
438            }
439
440            return( self::$includes[$path] );
441        }
442
443        public static function load( $path, $class = false )
444            {
445            if( $return = self::import( $path, "" ) )
446                return( $return );
447
448            if( !$class ){
449                preg_match( '/^\/?.*\/([^\/]+).php$/', $path, $class );
450                $class = $class[1];
451            }
452
453            $object =  self::$cache->get( $class );
454
455            if( !$object )
456            {
457                $object = new $class();
458                 self::$cache->put( $class, $object );
459            }
460
461            self::$includes[$path] = $object;
462
463            return( $object );
464        }
465
466        public static function wallet( $serviceName )
467        {
468            if( !isset( self::$wallet ) )
469            {
470                //// Hack //// TODO: passar o init da sessão no login do expresso
471                Config::init();
472
473                if(isset($_SESSION['wallet']))
474                    self::$wallet = $_SESSION['wallet'];
475                /////////////
476            }
477
478            return isset( self::$wallet[ $serviceName ] )? self::$wallet[ $serviceName ] : false;
479        }
480               
481       
482
483        public static function configure( $config, $newConfig )
484        {
485            foreach( $newConfig as $key => $value )
486                $config[$key] = $value;
487
488            return( $config );
489            }
490
491        public static function dispatch( $dispatcher, $data, $optionsMap = false )
492        {
493//          if( $mappedTo )
494//              $data = array( $mappedTo => $data );
495//
496//          foreach( $data as $method => $params )
497//          {
498// //           foreach( $data[ $method ] as $name => $value )
499//          }
500//
501//          self::import( "$dispatcher.php" );
502        }
503
504        //TODO: Compatibilizar as configs relativas aos modulos, adicionando os mesmo nos parametros passados
505        public static function service( $serviceName, $concept = false )
506        {
507            if( isset( self::$services[ $serviceName ] ) )
508                return self::$services[ $serviceName ];
509
510            if( !isset(self::$config[ $serviceName ]) )
511                 self::$config[ $serviceName ] = self::loadConfig( $serviceName, true );
512
513            if( !isset(self::$config[ $serviceName ]) )
514                return( false );
515
516            if( !isset(self::$config[ $serviceName ]['type']) )
517                self::$config[ $serviceName ]['type'] = 'service';
518
519            self::import( 'api/'.self::$config[ $serviceName ]['type'] );   //TODO: Item 4
520
521            $service = self::load( self::$config[ $serviceName ]['path'],
522                                   self::$config[ $serviceName ]['class'] );
523
524              $srvConfig = array();
525
526            if( isset(self::$config[ $serviceName ][ 'config' ]) )
527                $srvConfig = self::configure( $srvConfig, self::$config[ $serviceName ][ 'config' ] );
528            if( $wallet = self::wallet( $serviceName ) )
529                $srvConfig = self::configure( $srvConfig, $wallet );
530            if( $concept && isset(self::$config[ $concept ]['service.config']) )
531                $srvConfig = self::configure( $srvConfig, self::$config[ $concept ]['service.config'] );
532
533            if( empty( $srvConfig ) )
534                $srvConfig = false;
535
536            if( $service && self::$config[ $serviceName ]['type'] === 'service' )
537                self::connect( $service, $srvConfig );
538
539            return( self::$services[ $serviceName ] = $service );
540        }
541
542        //TODO: Compatibilizar as configs relativas aos modulos, adicionando os mesmo nos parametros passados
543        public static function interceptor( $method, $concept = false, $serviceName = false, $isService = false )
544        {
545            if( $concept && !isset(self::$config[ $concept ]) )
546              self::$config[ $concept ] = self::loadConfig( $concept );
547
548            if( !$concept ) $concept = 'global';
549            if( !$isService || !$serviceName ) $serviceName = 'global';
550
551            if( !isset( self::$interceptors[ $concept ] ) )
552                self::$interceptors[ $concept ] = array();
553
554            if( !isset( self::$interceptors[ $concept ][ $serviceName ] ) )
555                self::$interceptors[ $concept ][ $serviceName ] = array();
556
557            if( !isset( self::$interceptors[ $concept ][ $serviceName ][ $method ] ) )
558            {
559                $events = array( 'before', 'after' );
560                $interceptors = array();
561
562                $prefix = ( $isService )? "$serviceName." : "";
563
564                foreach( $events as $i => $event )
565                {
566                    $interceptors[$event] = array();
567
568                    if( !isset(self::$config[$concept]["$prefix$event.$method"]) )
569                      continue;
570
571                    foreach( self::$config[$concept]["$prefix$event.$method"] as $intercept => $interceptor )
572                            $interceptors[$event][$intercept] = self::load( $interceptor );
573                }
574
575                self::$interceptors[ $concept ][ $serviceName ][ $method ] = $interceptors;
576            }
577
578            return( self::$interceptors[ $concept ][ $serviceName ][ $method ] );
579        }
580
581//      public static function interceptorCommit( $eventType, $commitList, $isService = false )
582//      {
583//          $result = array( $eventType => array() );
584//         
585//          if( is_array( $commitList ) )
586//              foreach( $commitList as $i => $tx )
587//              {
588//                  $interceptors = self::interceptor( 'commit', $tx['concept'], $tx['service'], $isService );
589//       
590//                  $result[$eventType] = array_merge( $result[$eventType], $interceptors[$eventType] );
591//              }
592//
593//          return( $result );
594//      }
595
596//      public static function fire( $eventType, $method, &$params, $original, $isService = false )
597//      {
598//          if( $method === 'commit' )
599//              $interceptors = self::interceptorCommit( $eventType, $params['criteria'], $isService );
600//
601//          else
602//              $interceptors = self::interceptor( $method,
603//                                                 isset($original['URI']['concept']) ? $original['URI']['concept'] : false,
604//                                                 isset($params['URI']['service']) ? $params['URI']['service'] : false, $isService );
605//
606//          if( $interceptors && isset($interceptors[ $eventType ]) )
607//              foreach( $interceptors[ $eventType ] as $intercept => $interceptor )
608//              {
609//                  $return = $interceptor->$intercept( $params['URI'], $params['properties'], $params['criteria'], $original/*, $params['service']*/ );
610//
611//                  if( $return === false )
612//              return( false );
613//
614//                  if( isset($return) )
615//                      $params['properties'] = $return;
616//              }
617//
618//            return( $params );
619//      }
620
621        /*
622          * ex: array
623          *             (
624          *                     [0] array( 'OR', array( array( '=', 'campo', 'valor' ),
625                                                          array( 'OR', array( array( '=', 'campo', 'valor' ) ) ) )
626          *                     [1] array( '=', 'campo' , 'valor' )
627          *                     [2] array( 'OR' , array( array( '=' , campo', 'valor' ) ) )
628          *                     [3] array( 'IN', 'campo', array( '1' , '2' , '3' ) )
629          *             )
630          * OR
631          *         array( '=' , 'campo' , 'valor' )
632        */
633
634        //TODO: Compatibilizar as configs relativas aos modulos, adicionando os mesmo nos parametros passados
635        public static function serviceName( $URI, $original = false )
636        {
637             $concept = "";
638
639            if( $original && isset($original['concept']) && $original['concept'] )
640                $concept = $original['concept'];
641            elseif( isset($URI['concept']) && $URI['concept'] )
642                $concept = $URI['concept'];
643
644            if( ( !isset($URI['service']) || !$URI['service'] ) && $concept )
645            {
646                if( !isset(self::$config[ $concept ]) )
647                    self::$config[ $concept ] = self::loadConfig( $concept );
648
649                $URI['service'] = self::$config[ $concept ][ 'service' ];
650            }
651
652            if( !isset($URI['service']) )
653                throw new Exception( "CONFIGURATION ERROR: service name from concept '$concept' not found" );
654
655            return( $URI );
656        }
657}
658
659Controller::$cache = Controller::loadCache();
660?>
Note: See TracBrowser for help on using the repository browser.