Overview

Packages

  • awl
    • AuthPlugin
    • AwlDatabase
    • Browser
    • classEditor
    • DataEntry
    • DataUpdate
    • EMail
    • iCalendar
    • MenuSet
    • PgQuery
    • Session
    • Translation
    • User
    • Utilities
    • Validation
    • vCalendar
    • vComponent
    • XMLDocument
    • XMLElement
  • None

Classes

  • AuthPlugin
  • AwlCache
  • AwlDatabase
  • AwlDBDialect
  • AwlQuery
  • AwlUpgrader
  • Browser
  • BrowserColumn
  • DBRecord
  • Editor
  • EditorField
  • EMail
  • EntryField
  • EntryForm
  • iCalComponent
  • iCalProp
  • MenuOption
  • MenuSet
  • Multipart
  • PgQuery
  • Session
  • SinglePart
  • User
  • Validation
  • vCalendar
  • vComponent
  • vObject
  • vProperty
  • XMLDocument
  • XMLElement

Functions

  • _awl_connect_configured_database
  • _CompareMenuSequence
  • auth_external
  • auth_other_awl
  • awl_get_fields
  • awl_replace_sql_args
  • awl_set_locale
  • awl_version
  • BuildXMLTree
  • check_by_regex
  • check_temporary_passwords
  • clean_string
  • connect_configured_database
  • dbg_error_log
  • dbg_log_array
  • define_byte_mappings
  • deprecated
  • duration
  • fatal
  • force_utf8
  • getCacheInstance
  • gzdecode
  • i18n
  • init_gettext
  • olson_from_tzstring
  • param_to_global
  • qpg
  • quoted_printable_encode
  • replace_uri_params
  • session_salted_md5
  • session_salted_sha1
  • session_simple_md5
  • session_validate_password
  • sql_from_object
  • sql_from_post
  • trace_bug
  • translate
  • uuid
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
   1:    2:    3:    4:    5:    6:    7:    8:    9:   10:   11:   12:   13:   14:   15:   16:   17:   18:   19:   20:   21:   22:   23:   24:   25:   26:   27:   28:   29:   30:   31:   32:   33:   34:   35:   36:   37:   38:   39:   40:   41:   42:   43:   44:   45:   46:   47:   48:   49:   50:   51:   52:   53:   54:   55:   56:   57:   58:   59:   60:   61:   62:   63:   64:   65:   66:   67:   68:   69:   70:   71:   72:   73:   74:   75:   76:   77:   78:   79:   80:   81:   82:   83:   84:   85:   86:   87:   88:   89:   90:   91:   92:   93:   94:   95:   96:   97:   98:   99:  100:  101:  102:  103:  104:  105:  106:  107:  108:  109:  110:  111:  112:  113:  114:  115:  116:  117:  118:  119:  120:  121:  122:  123:  124:  125:  126:  127:  128:  129:  130:  131:  132:  133:  134:  135:  136:  137:  138:  139:  140:  141:  142:  143:  144:  145:  146:  147:  148:  149:  150:  151:  152:  153:  154:  155:  156:  157:  158:  159:  160:  161:  162:  163:  164:  165:  166:  167:  168:  169:  170:  171:  172:  173:  174:  175:  176:  177:  178:  179:  180:  181:  182:  183:  184:  185:  186:  187:  188:  189:  190:  191:  192:  193:  194:  195:  196:  197:  198:  199:  200:  201:  202:  203:  204:  205:  206:  207:  208:  209:  210:  211:  212:  213:  214:  215:  216:  217:  218:  219:  220:  221:  222:  223:  224:  225:  226:  227:  228:  229:  230:  231:  232:  233:  234:  235:  236:  237:  238:  239:  240:  241:  242:  243:  244:  245:  246:  247:  248:  249:  250:  251:  252:  253:  254:  255:  256:  257:  258:  259:  260:  261:  262:  263:  264:  265:  266:  267:  268:  269:  270:  271:  272:  273:  274:  275:  276:  277:  278:  279:  280:  281:  282:  283:  284:  285:  286:  287:  288:  289:  290:  291:  292:  293:  294:  295:  296:  297:  298:  299:  300:  301:  302:  303:  304:  305:  306:  307:  308:  309:  310:  311:  312:  313:  314:  315:  316:  317:  318:  319:  320:  321:  322:  323:  324:  325:  326:  327:  328:  329:  330:  331:  332:  333:  334:  335:  336:  337:  338:  339:  340:  341:  342:  343:  344:  345:  346:  347:  348:  349:  350:  351:  352:  353:  354:  355:  356:  357:  358:  359:  360:  361:  362:  363:  364:  365:  366:  367:  368:  369:  370:  371:  372:  373:  374:  375:  376:  377:  378:  379:  380:  381:  382:  383:  384:  385:  386:  387:  388:  389:  390:  391:  392:  393:  394:  395:  396:  397:  398:  399:  400:  401:  402:  403:  404:  405:  406:  407:  408:  409:  410:  411:  412:  413:  414:  415:  416:  417:  418:  419:  420:  421:  422:  423:  424:  425:  426:  427:  428:  429:  430:  431:  432:  433:  434:  435:  436:  437:  438:  439:  440:  441:  442:  443:  444:  445:  446:  447:  448:  449:  450:  451:  452:  453:  454:  455:  456:  457:  458:  459:  460:  461:  462:  463:  464:  465:  466:  467:  468:  469:  470:  471:  472:  473:  474:  475:  476:  477:  478:  479:  480:  481:  482:  483:  484:  485:  486:  487:  488:  489:  490:  491:  492:  493:  494:  495:  496:  497:  498:  499:  500:  501:  502:  503:  504:  505:  506:  507:  508:  509:  510:  511:  512:  513:  514:  515:  516:  517:  518:  519:  520:  521:  522:  523:  524:  525:  526:  527:  528:  529:  530:  531:  532:  533:  534:  535:  536:  537:  538:  539:  540:  541:  542:  543:  544:  545:  546:  547:  548:  549:  550:  551:  552:  553:  554:  555:  556:  557:  558:  559:  560:  561:  562:  563:  564:  565:  566:  567:  568:  569:  570:  571:  572:  573:  574:  575:  576:  577:  578:  579:  580:  581:  582:  583:  584:  585:  586:  587:  588:  589:  590:  591:  592:  593:  594:  595:  596:  597:  598:  599:  600:  601:  602:  603:  604:  605:  606:  607:  608:  609:  610:  611:  612:  613:  614:  615:  616:  617:  618:  619:  620:  621:  622:  623:  624:  625:  626:  627:  628:  629:  630:  631:  632:  633:  634:  635:  636:  637:  638:  639:  640:  641:  642:  643:  644:  645:  646:  647:  648:  649:  650:  651:  652:  653:  654:  655:  656:  657:  658:  659:  660:  661:  662:  663:  664:  665:  666:  667:  668:  669:  670:  671:  672:  673:  674:  675:  676:  677:  678:  679:  680:  681:  682:  683:  684:  685:  686:  687:  688:  689:  690:  691:  692:  693:  694:  695:  696:  697:  698:  699:  700:  701:  702:  703:  704:  705:  706:  707:  708:  709:  710:  711:  712:  713:  714:  715:  716:  717:  718:  719:  720:  721:  722:  723:  724:  725:  726:  727:  728:  729:  730:  731:  732:  733:  734:  735:  736:  737:  738:  739:  740:  741:  742:  743:  744:  745:  746:  747:  748:  749:  750:  751:  752:  753:  754:  755:  756:  757:  758:  759:  760:  761:  762:  763:  764:  765:  766:  767:  768:  769:  770:  771:  772:  773:  774:  775:  776:  777:  778:  779:  780:  781:  782:  783:  784:  785:  786:  787:  788:  789:  790:  791:  792:  793:  794:  795:  796:  797:  798:  799:  800:  801:  802:  803:  804:  805:  806:  807:  808:  809:  810:  811:  812:  813:  814:  815:  816:  817:  818:  819:  820:  821:  822:  823:  824:  825:  826:  827:  828:  829:  830:  831:  832:  833:  834:  835:  836:  837:  838:  839:  840:  841:  842:  843:  844:  845:  846:  847:  848:  849:  850:  851:  852:  853:  854:  855:  856:  857:  858:  859:  860:  861:  862:  863:  864:  865:  866:  867:  868:  869:  870:  871:  872:  873:  874:  875:  876:  877:  878:  879:  880:  881:  882:  883:  884:  885:  886:  887:  888:  889:  890:  891:  892:  893:  894:  895:  896:  897:  898:  899:  900:  901:  902:  903:  904:  905:  906:  907:  908:  909:  910:  911:  912:  913:  914:  915:  916:  917:  918:  919:  920:  921:  922:  923:  924:  925:  926:  927:  928:  929:  930:  931:  932:  933:  934:  935:  936:  937:  938:  939:  940:  941:  942:  943:  944:  945:  946:  947:  948:  949:  950:  951:  952:  953:  954:  955:  956:  957:  958:  959:  960:  961:  962:  963:  964:  965:  966:  967:  968:  969:  970:  971:  972:  973:  974:  975:  976:  977:  978:  979:  980:  981:  982:  983:  984:  985:  986:  987:  988:  989:  990:  991:  992:  993:  994:  995:  996:  997:  998:  999: 1000: 1001: 
<?php
/**
 * A Class for handling vCalendar & vCard data.
 *
 * When parsed the underlying structure is roughly as follows:
 *
 *   vComponent( array(vComponent), array(vProperty) )
 *
 * @package awl
 * @subpackage vComponent
 * @author Milan Medlik <milan@morphoss.com>
 * @copyright Morphoss Ltd <http://www.morphoss.com/>
 * @license   http://gnu.org/copyleft/lgpl.html GNU LGPL v2 or later
 *
 */

    include_once('vObject.php');
    //include_once('HeapLines.php');
    include_once('vProperty.php');

    class vComponent extends vObject{

        private $components;
        private $properties;
        private $type;
        private $iterator;
        private $seekBegin;
        private $seekEnd;
        private $propertyLocation;

        const KEYBEGIN = 'BEGIN:';
        const KEYBEGINLENGTH = 6;
        const KEYEND = "END:";
        const KEYENDLENGTH = 4;
        const VEOL = "\r\n";

        public static $PREPARSED = false;

        function __construct($propstring=null, &$refData=null){
            parent::__construct($master);

            unset($this->type);

            if(isset($propstring) && gettype($propstring) == 'string'){
                $this->initFromText($propstring);
            } else if(isset($refData)){
                if(gettype($refData) == 'string'){
                    $this->initFromText($refData);
                } else if(gettype($refData) == 'object') {
                    $this->initFromIterator($refData);
                }
            } else {
                //$text = '';
                //$this->initFromText($text);
            }


//            if(isset($this->iterator)){
//                $this->parseFrom($this->iterator);
//            }


        }

        function initFromIterator(&$iterator, $begin = -1){
            $this->iterator = &$iterator;

            //$this->seekBegin = $this->iterator->key();



            $iterator = $this->iterator;
            do {
                $line = $iterator->current();
                $seek = $iterator->key();

                $posStart = strpos(strtoupper($line), vComponent::KEYBEGIN);
                if($posStart !== false && $posStart == 0){
                    if(!isset($this->type)){
                        $this->seekBegin = $seek;

                        $this->type = strtoupper(substr($line, vComponent::KEYBEGINLENGTH));
                    }
                } else {

                    $posEnd = strpos(strtoupper($line), vComponent::KEYEND);
                    if($posEnd !== false && $posEnd == 0){
                        $thisEnd = strtoupper(substr($line, vComponent::KEYENDLENGTH));
                        if($thisEnd == $this->type){
                            $this->seekEnd = $seek;
                            //$iterator->next();
                            $len = strlen($this->type);
                            $last = $this->type[$len-1];
                            if($last == "\r"){
                                $this->type = strtoupper(substr($this->type, 0, $len-1));
                            }
                            break;
                        }

                    } else {
                        //$this->properties[] = new vProperty(null, $iterator, $seek);
                    }
                }




                $iterator->next();
            } while($iterator->valid());
            //$this->parseFrom($iterator);

        }

        public function getIterator(){
            return $this->iterator;
        }

        function initFromText(&$plainText){
            $plain2 = $this->UnwrapComponent($plainText);

            //$file = fopen('data.out.tmp', 'w');
            //$plain3 = preg_replace('{\r?\n}', '\r\n', $plain2 );
            //fwrite($file, $plain2);
            //fclose($file);
            //$lines = &explode(PHP_EOL, $plain2);
//            $arrayData = new ArrayObject($lines);
//            $this->iterator = &$arrayData->getIterator();
//            $this->initFromIterator($this->iterator, 0);
//            unset($plain);
//            unset($iterator);
//            unset($arrayData);
//            unset($lines);

            // Note that we can't use PHP_EOL here, since the line splitting should handle *either* of CR, CRLF or LF line endings.
            $arrayOfLines = new ArrayObject(preg_split('{\r?\n}', $plain2));
            $this->iterator = $arrayOfLines->getIterator();
            unset($plain2);
            //$this->initFromIterator($this->iterator);
            //$this->iterator = new HeapLines($plain);

            //$this->initFromIterator(new HeapLines($plain), 0);
            $this->parseFrom($this->iterator);

        }

        function rewind(){
            if(isset($this->iterator) && isset($this->seekBegin)){
                $this->iterator->seek($this->seekBegin);
            }
        }


        /**
         * fill arrays with components and properties if they are empty.
         *
         * basicaly the object are just pointer to memory with input data
         * (iterator with seek address on start and end)
         * but when you want get information of any components
         * or properties is necessary call this function first
         *
         * @see GetComponents(), ComponentsCount(), GetProperties(), PropertiesCount()
         */
        function explode(){
            if(!isset($this->properties) && !isset($this->components) && $this->isValid()){
                unset($this->properties);
                unset($this->components);
                unset($this->type);
                $this->rewind();
                $this->parseFrom($this->iterator);
            }
        }

        function close(){

            if(isset($this->components)){
                foreach($this->components as $comp){
                    $comp->close();
                }
            }

            if($this->isValid()){
                unset($this->properties);
                unset($this->components);
            }



        }

        function parseFrom(&$iterator){


            $begin = $iterator->key();
            $typelen = 0;
            //$count = $lines->count();

            do{
                $line = $iterator->current();
                //$line = substr($current, 0, strlen($current) -1);
                $end = $iterator->key();

                $pos = strpos(strtoupper($line), vComponent::KEYBEGIN);
                $callnext = true;
                if($pos !== false && $pos == 0) {
                    $type = strtoupper(substr($line, vComponent::KEYBEGINLENGTH));

                    if($typelen !== 0 && strncmp($this->type, $type, $typelen) !== 0){
                        $this->components[] = new vComponent(null, $iterator);
                        $callnext = false;
                    } else {
                        // in special cases when is "\r" on end remove it
                        // We should probably throw an error if we get here, because the
                        // thing that splits stuff should not be giving us this.
                        $typelen = strlen($type);
                        if($type[$typelen-1] == "\r"){
                            $typelen--;
                            $this->type = substr($type, 0, $typelen);
                        } else {
                            $this->type = $type;
                        }


                        //$iterator->offsetUnset($end);
                        //$iterator->seek($begin);
                        //$callnext = false;
                    }

                } else {
                    $pos = strpos(strtoupper($line), vComponent::KEYEND);

                    if($pos !== false && $pos == 0) {
                        $this->seekBegin = $begin;
                        $this->seekEnd = $end;
                        //$iterator->offsetUnset($end);
                        //$iterator->seek($end-2);
                        //$line2 = $iterator->current();
                        //$this->seekEnd = $iterator->key();

                        //$callnext = false;
                        //$newheap = $lines->createLineHeapFrom($start, $end);
                        //$testfistline = $newheap->substr(0);
                        //echo "end:" . $this->key . "[$start, $end]<br>";
                        //$lines->nextLine();
                        //$iterator->offsetUnset($end);
                        return;
                    } else {
//                    $prstart = $lines->getSwheretartLineOnHeap();
//                    $prend =
//$this->properties[] = new vProperty("AHOJ");
                        $parameters = preg_split( '(:|;)', $line);
                        $possiblename = strtoupper(array_shift( $parameters ));
                        $this->properties[] = new vProperty($possiblename, $this->master, $iterator, $end);
                        //echo $this->key . ' property line' . "[$prstart,$prend]<br>";

                    }
                }

//                if($callnext){
//                    $iterator->next();
//                }
                //if($callnext)
                //    $iterator->offsetUnset($end);
                if($iterator->valid())
                    $iterator->next();

            } while($iterator->valid() && ( !isset($this->seekEnd) || $this->seekEnd > $end ) );
            //$lines->getEndLineOnHeap();


        }



        /**
         * count of component
         * @return int
         */
        public function ComponentCount(){
            $this->explode();
            return isset($this->components) ? count($this->components) : 0;
        }

        /**
         * count of component
         * @return int
         */
        public function propertiesCount(){
            $this->explode();
            return isset($this->properties) ? count($this->properties) : 0;
        }

        /**
         * @param $position
         * @return null - whet is position out of range
         */
        public function getComponentAt($position){
            $this->explode();
            if($this->ComponentCount() > $position){
                return $this->components[$position];
            } else {
                return null;
            }
        }

        function getPropertyAt($position){
            $this->explode();
            if($this->propertiesCount() > $position){
                return $this->properties[$position];
            } else {
                return null;
            }

        }

        function clearPropertyAt($position) {
            $this->explode();
            if($this->isValid()){
                $this->invalidate();
            }

            $i=0;  // property index/position is relative to current vComponent
            foreach( $this->properties AS $k => $v ) {
                if ( $i == $position ) {
                    unset($this->properties[$k]);
                }
                $i++;
            }
            $this->properties = array_values($this->properties);
        }


        /**
         * Return the type of component which this is
         */
        function GetType() {
            return $this->type;
        }


        /**
         * Set the type of component which this is
         */
        function SetType( $type ) {
            if ( $this->isValid() ) {
                $this->invalidate();
            };
            $this->type = strtoupper($type);
            return $this->type;
        }


        /**
         * Collect an array of all parameters of our properties which are the specified type
         * Mainly used for collecting the full variety of references TZIDs
         */
        function CollectParameterValues( $parameter_name ) {
            $this->explode();
            $values = array();
            if(isset($this->components)){
                foreach( $this->components AS $k => $v ) {
                    $also = $v->CollectParameterValues($parameter_name);
                    $values = array_merge( $values, $also );
                }
            }
            if(isset($this->properties)){
                foreach( $this->properties AS $k => $v ) {
                    $also = $v->GetParameterValue($parameter_name);
                    if ( isset($also) && $also != "" ) {
//        dbg_error_log( 'vComponent', "::CollectParameterValues(%s) : Found '%s'", $parameter_name, $also);
                        $values[$also] = 1;
                    }
                }
            }

            return $values;
        }


        /**
         * Return the first instance of a property of this name
         */
        function GetProperty( $type ) {
            $this->explode();
            foreach( $this->properties AS $k => $v ) {
                if ( is_object($v) && $v->Name() == $type ) {
                    return $v;
                }
                else if ( !is_object($v) ) {
                    dbg_error_log("ERROR", 'vComponent::GetProperty(): Trying to get %s on %s which is not an object!', $type, $v );
                }
            }
            /** So we can call methods on the result of this, make sure we always return a vProperty of some kind */
            return null;
        }

        /**
         * Return the value of the first instance of a property of this name, or null
         */
        function GetPValue( $type ) {
            $this->explode();
            $p = $this->GetProperty($type);
            if ( isset($p) ) return $p->Value();
            return null;
        }


        /**
         * Get all properties, or the properties matching a particular type, or matching an
         * array associating property names with true values: array( 'PROPERTY' => true, 'PROPERTY2' => true )
         */
        function GetProperties( $type = null ) {
            // the properties in base are with name
            // it was setted in parseFrom(&interator)
            if(!isset($this->properties)){
                $this->explode();
            }
            $properties = array();
            $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
            foreach( $this->properties AS $k => $v ) {
                $name = $v->Name(); //get Property name (string)
                $name = preg_replace( '/^.*[.]/', '', $name ); //for grouped properties we remove prefix itemX.
                if ( $type == null || (isset($testtypes[$name]) && $testtypes[$name])) {
                    $properties[] = $v;
                }
            }
            return $properties;
        }

        /**
         * Return an array of properties matching the specified path
         *
         * @return array An array of vProperty within the tree which match the path given, in the form
         *  [/]COMPONENT[/...]/PROPERTY in a syntax kind of similar to our poor man's XML queries. We
         *  also allow COMPONENT and PROPERTY to be !COMPONENT and !PROPERTY for ++fun.
         *
         * @note At some point post PHP4 this could be re-done with an iterator, which should be more efficient for common use cases.
         */
        function GetPropertiesByPath( $path ) {
            $properties = array();
            dbg_error_log( 'vComponent', "GetPropertiesByPath: Querying within '%s' for path '%s'", $this->type, $path );
            if ( !preg_match( '#(/?)(!?)([^/]+)(/?.*)$#', $path, $matches ) ) return $properties;

            $anchored = ($matches[1] == '/');
            $inverted = ($matches[2] == '!');
            $ourtest = $matches[3];
            $therest = $matches[4];
            dbg_error_log( 'vComponent', "GetPropertiesByPath: Matches: %s -- %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3], $matches[4] );
            if ( $ourtest == '*' || (($ourtest == $this->type) !== $inverted) && $therest != '' ) {
                if ( preg_match( '#^/(!?)([^/]+)$#', $therest, $matches ) ) {
                    $normmatch = ($matches[1] =='');
                    $proptest  = $matches[2];

                    $thisproperties = $this->GetProperties();
                    if(isset($thisproperties) && count($thisproperties) > 0){
                        foreach( $thisproperties AS $k => $v ) {
                            if ( $proptest == '*' || (($v->Name() == $proptest) === $normmatch ) ) {
                                $properties[] = $v;
                            }
                        }
                    }

                }
                else {
                    /**
                     * There is more to the path, so we recurse into that sub-part
                     */
                    foreach( $this->GetComponents() AS $k => $v ) {
                        $properties = array_merge( $properties, $v->GetPropertiesByPath($therest) );
                    }
                }
            }

            if ( ! $anchored ) {
                /**
                 * Our input $path was not rooted, so we recurse further
                 */
                foreach( $this->GetComponents() AS $k => $v ) {
                    $properties = array_merge( $properties, $v->GetPropertiesByPath($path) );
                }
            }
            dbg_error_log('vComponent', "GetPropertiesByPath: Found %d within '%s' for path '%s'\n", count($properties), $this->type, $path );
            return $properties;
        }

        /**
         * Clear all properties, or the properties matching a particular type
         * @param string|array $type The type of property - omit for all properties - or an
         * array associating property names with true values: array( 'PROPERTY' => true, 'PROPERTY2' => true )
         */
        function ClearProperties( $type = null ) {
            $this->explode();
            if($this->isValid()){
                $this->invalidate();
            }

            if ( $type != null ) {
                $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
                // First remove all the existing ones of that type
                foreach( $this->properties AS $k => $v ) {
                    if ( isset($testtypes[$v->Name()]) && $testtypes[$v->Name()] ) {
                        unset($this->properties[$k]);

                    }
                }
                $this->properties = array_values($this->properties);
            }
            else {

                $this->properties = array();
            }
        }

        /**
         * Set all properties, or the ones matching a particular type
         */
        function SetProperties( $new_properties, $type = null ) {
            $this->explode();
            $this->ClearProperties($type);
            foreach( $new_properties AS $k => $v ) {
                $this->properties[] = $v;
            }
        }

        /**
         * Adds a new property
         *
         * @param vProperty $new_property The new property to append to the set, or a string with the name
         * @param string $value The value of the new property (default: param 1 is an vProperty with everything
         * @param array $parameters The key/value parameter pairs (default: none, or param 1 is an vProperty with everything)
         */
        function AddProperty( $new_property, $value = null, $parameters = null ) {
            $this->explode();
            if ( isset($value) && gettype($new_property) == 'string' ) {
                $new_prop = new vProperty('', $this->master);
                $new_prop->Name($new_property);
                $new_prop->Value($value);
                if ( $parameters != null ) {
                    $new_prop->Parameters($parameters);
                }
//      dbg_error_log('vComponent'," Adding new property '%s'", $new_prop->Render() );
                $this->properties[] = $new_prop;
            }
            else if ( $new_property instanceof vProperty ) {
                $this->properties[] = $new_property;
                $new_property->setMaster($this->master);
            }

            if($this->isValid()){
                $this->invalidate();
            }
        }

        /**
         * Get all sub-components, or at least get those matching a type, or failling to match,
         * should the second parameter be set to false. Component types may be a string or an array
         * associating property names with true values: array( 'TYPE' => true, 'TYPE2' => true )
         *
         * @param mixed $type The type(s) to match (default: All)
         * @param boolean $normal_match Set to false to invert the match (default: true)
         * @return array an array of the sub-components
         */
        function GetComponents( $type = null, $normal_match = true ) {
            $this->explode();
            $components = isset($this->components) ? $this->components : array();

            if ( $type != null ) {
                //$components = $this->components;
                $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
                foreach( $components AS $k => $v ) {
//        printf( "Type: %s, %s, %s\n", $v->GetType(),
//                 ($normal_match && isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] ? 'true':'false'),
//                 ( !$normal_match && (!isset($testtypes[$v->GetType()]) || !$testtypes[$v->GetType()]) ? 'true':'false')
//               );
                    if ( !($normal_match && isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] )
                        && !( !$normal_match && (!isset($testtypes[$v->GetType()]) || !$testtypes[$v->GetType()])) ) {
                        unset($components[$k]);
                    }
                }
                $components = array_values($components);
            }
//    print_r($components);
            return $components;
        }


        /**
         * Clear all components, or the components matching a particular type
         * @param string $type The type of component - omit for all components
         */
        function ClearComponents( $type = null ) {
            if($this->isValid()){
                $this->explode();
            }


            if ( $type != null && !empty($this->components)) {
                $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
                // First remove all the existing ones of that type
                foreach( $this->components AS $k => $v ) {
                    $this->components[$k]->ClearComponents($testtypes);
                    if ( isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] ) {
                        unset($this->components[$k]);
                        if ( $this->isValid()) {
                            $this->invalidate();
                        }
                    }

                }
            }
            else {
                $this->components = array();
                if ( $this->isValid()) {
                    $this->invalidate();
                }
            }

            return $this->isValid();
        }

        /**
         * Sets some or all sub-components of the component to the supplied new components
         *
         * @param array of vComponent $new_components The new components to replace the existing ones
         * @param string $type The type of components to be replaced.  Defaults to null, which means all components will be replaced.
         */
        function SetComponents( $new_component, $type = null ) {
            $this->explode();
            if ( $this->isValid()) {
                $this->invalidate();
            }
            if ( empty($type) ) {
                $this->components = $new_component;
                return;
            }

            $this->ClearComponents($type);
            foreach( $new_component AS $k => $v ) {
                $this->components[] = $v;
            }
        }

        /**
         * Adds a new subcomponent
         *
         * @param vComponent $new_component The new component to append to the set
         */
        public function AddComponent( $new_component ) {
            $this->explode();
            if ( is_array($new_component) && count($new_component) == 0 ) return;

            if ( $this->isValid()) {
                $this->invalidate();
            }

            try {
                if ( is_array($new_component) ) {
                    foreach( $new_component AS $k => $v ) {
                        $this->components[] = $v;
                        if ( !method_exists($v,'setMaster') ) fatal('Component to be added must be a vComponent');
                        $v->setMaster($this->master);
                    }
                }
                else {
                    if ( !method_exists($new_component,'setMaster') ) fatal('Component to be added must be a vComponent');
                    $new_component->setMaster($this->master);
                    $this->components[] = $new_component;
                }
            }
            catch( Exception $e ) {
                fatal();
            }
        }


        /**
         * Mask components, removing any that are not of the types in the list
         * @param array $keep An array of component types to be kept
         * @param boolean $recursive (default true) Whether to recursively MaskComponents on the ones we find
         */
        function MaskComponents( $keep, $recursive = true ) {
            $this->explode();
            if(!isset($this->components)){
                return ;
            }

            foreach( $this->components AS $k => $v ) {
                if ( !isset($keep[$v->GetType()]) ) {
                    unset($this->components[$k]);
                    if ( $this->isValid()) {
                        $this->invalidate();
                    }
                }
                else if ( $recursive ) {
                    $v->MaskComponents($keep);
                }
            }
        }

        /**
         * Mask properties, removing any that are not in the list
         * @param array $keep An array of property names to be kept
         * @param array $component_list An array of component types to check within
         */
        function MaskProperties( $keep, $component_list=null ) {
            $this->explode();
            if ( !isset($component_list) || isset($component_list[$this->type]) ) {
                foreach( $this->properties AS $k => $v ) {
                    if ( !isset($keep[$v->Name()]) || !$keep[$v->Name()] ) {
                        unset($this->properties[$k]);
                        if ( $this->isValid()) {
                            $this->invalidate();
                        }
                    }
                }
            }
            if(isset($this->components)){
                foreach( $this->components AS $k => $v ) {
                    $v->MaskProperties($keep, $component_list);
                }
            }

        }

        /**
         * This imposes the (CRLF + linear space) wrapping specified in RFC2445. According
         * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
         * XML parsers often muck with it and may remove the CR.  We output RFC2445 compliance.
         *
         * In order to preserve pre-existing wrapping in the component, we split the incoming
         * string on line breaks before running wordwrap over each component of that.
         */
        function WrapComponent( $content ) {
            $strs = preg_split( "/\r?\n/", $content );
            $wrapped = "";
            foreach ($strs as $str) {
//                print "Before: >>$str<<, len(".strlen($str).")\n";
                $wrapped_bit = (strlen($str) < 76 ? $str : preg_replace( '/(.{72})/u', '$1'."\r\n ", $str )) .self::VEOL;
//                print "After: >>$wrapped_bit<<\n";
                $wrapped .= $wrapped_bit;
            }
            return $wrapped;
        }

        /**
         * This unescapes the (CRLF + linear space) wrapping specified in RFC2445. According
         * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
         * XML parsers often muck with it and may remove the CR.  We accept either case.
         */
        function UnwrapComponent( &$content ) {
            return preg_replace('/\r?\n[ \t]/', '', $content );
        }


        /**
         * Render vComponent without wrap lines
         * @param null $restricted_properties
         * @param bool $force_rendering
         * @return string
         */
        protected function RenderWithoutWrap($restricted_properties = null, $force_rendering = false){
            $unrolledComponents = isset($this->components);
            $rendered = vComponent::KEYBEGIN . $this->type . self::VEOL;


            if($this->isValid()){
                $rendered .= $this->RenderWithoutWrapFromIterator($unrolledComponents);
            } else {
                $rendered .= $this->RenderWithoutWrapFromObjects();
            }

            if($unrolledComponents){
                //$count = 0;
                foreach($this->components as $component){
                    //$component->explode();
                    //$count++;
                    $component_render = $component->RenderWithoutWrap( null, $force_rendering );
                    if(strlen($component_render) > 0){
                        $rendered .= $component_render . self::VEOL;
                    }

                    //$component->close();

                }
            }

            return $rendered . vComponent::KEYEND . $this->type;
        }

        /**
         * Let render property by property
         * @return string
         */
        protected function RenderWithoutWrapFromObjects(){
            $rendered = '';
            if(isset($this->properties)){
                foreach( $this->properties AS $k => $v ) {
                    if ( method_exists($v, 'Render') ) {
                        $forebug = $v->Render() . self::VEOL;
                        $rendered .= $forebug;
                    }
                }
            }

            return $rendered;
        }

        /**
         * take source data in Iterator and recreate to string
         * @param boolean $unroledComponents - have any components
         * @return string - rendered object
         */
        protected function RenderWithoutWrapFromIterator($unrolledComponents){
            $this->rewind();
            $rendered = '';
            $lentype = 0;

            if(isset($this->type)){
                $lentype = strlen($this->type);
            }

            $iterator = $this->iterator;
            $inInnerObject = 0;
            do {
                $line = $iterator->current() . self::VEOL;
                $seek = $iterator->key();

                $posStart = strpos($line, vComponent::KEYBEGIN);
                if($posStart !== false && $posStart == 0){
                    $type = substr($line, vComponent::KEYBEGINLENGTH);
                    if(!isset($this->type)){
                        //$this->seekBegin = $seek;
                        $this->type = $type;
                        $lentype = strlen($this->type);
                    } else if(strncmp($type, $this->type, $lentype) != 0){
                        // dont render line which is owned
                        // by inner commponent -> inner component *BEGIN*
                        if($unrolledComponents){
                            $inInnerObject++;
                        } else {
                            $rendered .= $line ;
                        }
                    }
                } else {

                    $posEnd = strpos($line, vComponent::KEYEND);
                    if($posEnd !== false && $posEnd == 0){
                        $thisEnd = substr($line, vComponent::KEYENDLENGTH);
                        if(strncmp($thisEnd, $this->type, $lentype) == 0){
                            // Current object end
                            $this->seekEnd = $seek;
                            //$iterator->next();
                            break;
                        }else if($unrolledComponents){
                            // dont render line which is owned
                            // by inner commponent -> inner component *END*
                            $inInnerObject--;
                        } else {
                            $rendered .= $line;
                        }

                    } else if($inInnerObject === 0 || !$unrolledComponents){
                        $rendered .= $line;
                    }
                }
                $iterator->next();
            } while($iterator->valid() && ( !isset($this->seekEnd) || $this->seekEnd > $seek));


            return $rendered;

        }


        /**
         * render object to string with wraped lines
         * @param null $restricted_properties
         * @param bool $force_rendering
         * @return string - rendered object
         */
        function Render($restricted_properties = null, $force_rendering = false){
            return $this->WrapComponent($this->RenderWithoutWrap($restricted_properties, $force_rendering));
            //return $this->InternalRender($restricted_properties, $force_rendering);
        }

        function isValid(){
            if($this->valid){
                if(isset($this->components)){
                    foreach($this->components as $comp){
                        if(!$comp->isValid()){
                            return false;
                        }
                    }
                }

                return true;
            }
            return false;
        }

 /**
   * Test a PROP-FILTER or COMP-FILTER and return a true/false
   * COMP-FILTER (is-defined | is-not-defined | (time-range?, prop-filter*, comp-filter*))
   * PROP-FILTER (is-defined | is-not-defined | ((time-range | text-match)?, param-filter*))
   *
   * @param array $filter An array of XMLElement defining the filter
   *
   * @return boolean Whether or not this vComponent passes the test
   */
  function TestFilter( $filters ) {
    foreach( $filters AS $k => $v ) {
      $tag = $v->GetNSTag();
//      dbg_error_log( 'vCalendar', ":TestFilter: '%s' ", $tag );
      switch( $tag ) {
        case 'urn:ietf:params:xml:ns:caldav:is-defined':
        case 'urn:ietf:params:xml:ns:carddav:is-defined':
          if ( count($this->properties) == 0 && count($this->components) == 0 ) return false;
          break;

        case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
        case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
          if ( count($this->properties) > 0 || count($this->components) > 0 ) return false;
          break;

        case 'urn:ietf:params:xml:ns:caldav:comp-filter':
        case 'urn:ietf:params:xml:ns:carddav:comp-filter':
          $subcomponents = $this->GetComponents($v->GetAttribute('name'));
          $subfilter = $v->GetContent();
//          dbg_error_log( 'vCalendar', ":TestFilter: Found '%d' (of %d) subs of type '%s'",
//                       count($subcomponents), count($this->components), $v->GetAttribute('name') );
          $subtag = $subfilter[0]->GetNSTag();
          if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-not-defined'
            || $subtag == 'urn:ietf:params:xml:ns:carddav:is-not-defined' ) {
            if ( count($properties) > 0 ) {
//              dbg_error_log( 'vComponent', ":TestFilter: Wanted none => false" );
              return false;
            }
          }
          else if ( count($subcomponents) == 0 ) {
            if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-defined'
              || $subtag == 'urn:ietf:params:xml:ns:carddav:is-defined' ) {
//              dbg_error_log( 'vComponent', ":TestFilter: Wanted some => false" );
              return false;
            }
            else {
//              dbg_error_log( 'vCalendar', ":TestFilter: Wanted something from missing sub-components => false" );
              $negate = $subfilter[0]->GetAttribute("negate-condition");
              if ( empty($negate) || strtolower($negate) != 'yes' ) return false;
            }
          }
          else {
            foreach( $subcomponents AS $kk => $subcomponent ) {
              if ( ! $subcomponent->TestFilter($subfilter) ) return false;
            }
          }
          break;

        case 'urn:ietf:params:xml:ns:carddav:prop-filter':
        case 'urn:ietf:params:xml:ns:caldav:prop-filter':
          $subfilter = $v->GetContent();
          $properties = $this->GetProperties($v->GetAttribute("name"));
          dbg_error_log( 'vCalendar', ":TestFilter: Found '%d' props of type '%s'", count($properties), $v->GetAttribute('name') );
          if ( empty($subfilter) ) {
            // empty means: test if a property of the type specified by the "name" attribute exists (rfc6352, 10.5.1)
            $subtag = str_replace('prop-filter', 'is-defined', $tag);
          } else {
            $subtag = $subfilter[0]->GetNSTag();
          }
          if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-not-defined'
            || $subtag == 'urn:ietf:params:xml:ns:carddav:is-not-defined' ) {
            if ( count($properties) > 0 ) {
//              dbg_error_log( 'vCalendar', ":TestFilter: Wanted none => false" );
              return false;
            }
          }
          else if ( count($properties) == 0 ) {
            if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-defined'
              || $subtag == 'urn:ietf:params:xml:ns:carddav:is-defined' ) {
//              dbg_error_log( 'vCalendar', ":TestFilter: Wanted some => false" );
              return false;
            }
            else {
//              dbg_error_log( 'vCalendar', ":TestFilter: Wanted '%s' from missing sub-properties => false", $subtag );
              $negate = $subfilter[0]->GetAttribute("negate-condition");
              if ( empty($negate) || strtolower($negate) != 'yes' ) return false;
            }
          }
          else {
            foreach( $properties AS $kk => $property ) {
              if ( !empty($subfilter) && !$property->TestFilter($subfilter) ) return false;
            }
          }
          break;
      }
    }
    return true;
  }

    }


AWL API documentation generated by ApiGen