@@ -36,7 +36,7 @@ |
||
| 36 | 36 | } |
| 37 | 37 | |
| 38 | 38 | public function __toString() { |
| 39 | - return "MismatchedSetException(".$this->getUnexpectedType()."!=".$this->expecting.")"; |
|
| 39 | + return "MismatchedSetException(" . $this->getUnexpectedType() . "!=" . $this->expecting . ")"; |
|
| 40 | 40 | } |
| 41 | 41 | } |
| 42 | 42 | |
@@ -1,30 +1,30 @@ |
||
| 1 | 1 | <?php |
| 2 | 2 | //Todo: find a decent set implementation for php |
| 3 | - class Set{ |
|
| 4 | - public function __construct($arr){ |
|
| 3 | + class Set { |
|
| 4 | + public function __construct($arr) { |
|
| 5 | 5 | $this->store = array(); |
| 6 | - foreach($arr as $el){ |
|
| 6 | + foreach ($arr as $el) { |
|
| 7 | 7 | $this->store[$el] = $el; |
| 8 | 8 | } |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | - public function add($value){ |
|
| 11 | + public function add($value) { |
|
| 12 | 12 | $this->store[$value] = $value; |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | - public function member($value){ |
|
| 15 | + public function member($value) { |
|
| 16 | 16 | return array_key_exists($value, $this->store); |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | - public function union($otherSet){ |
|
| 19 | + public function union($otherSet) { |
|
| 20 | 20 | return new Set(array_merge($this->store, $otherSet->store)); |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | - public function unionInPlace($otherSet){ |
|
| 23 | + public function unionInPlace($otherSet) { |
|
| 24 | 24 | $this->store = $this->union($otherSet)->store; |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | - public function remove($value){ |
|
| 27 | + public function remove($value) { |
|
| 28 | 28 | unset($this->store[$value]); |
| 29 | 29 | } |
| 30 | 30 | } |
@@ -59,7 +59,7 @@ discard block |
||
| 59 | 59 | */ |
| 60 | 60 | class RecognitionException extends Exception { |
| 61 | 61 | |
| 62 | - public $line=0; |
|
| 62 | + public $line = 0; |
|
| 63 | 63 | |
| 64 | 64 | |
| 65 | 65 | public function __construct($input) { |
@@ -72,41 +72,41 @@ discard block |
||
| 72 | 72 | * can retrieve the ith Token, we have to track the Token object. |
| 73 | 73 | * For parsers. Even when it's a tree parser, token might be set. |
| 74 | 74 | */ |
| 75 | - $this->token=null; |
|
| 75 | + $this->token = null; |
|
| 76 | 76 | |
| 77 | 77 | /** If this is a tree parser exception, node is set to the node with |
| 78 | 78 | * the problem. |
| 79 | 79 | */ |
| 80 | - $this->node=null; |
|
| 80 | + $this->node = null; |
|
| 81 | 81 | |
| 82 | 82 | /** The current char when an error occurred. For lexers. */ |
| 83 | - $this->c=0; |
|
| 83 | + $this->c = 0; |
|
| 84 | 84 | |
| 85 | 85 | /** Track the line at which the error occurred in case this is |
| 86 | 86 | * generated from a lexer. We need to track this since the |
| 87 | 87 | * unexpected char doesn't carry the line info. |
| 88 | 88 | */ |
| 89 | - $this->line=0; |
|
| 89 | + $this->line = 0; |
|
| 90 | 90 | |
| 91 | - $this->charPositionInLine=0; |
|
| 91 | + $this->charPositionInLine = 0; |
|
| 92 | 92 | |
| 93 | 93 | /** If you are parsing a tree node stream, you will encounter som |
| 94 | 94 | * imaginary nodes w/o line/col info. We now search backwards looking |
| 95 | 95 | * for most recent token with line/col info, but notify getErrorHeader() |
| 96 | 96 | * that info is approximate. |
| 97 | 97 | */ |
| 98 | - $this->approximateLineInfo=false; |
|
| 98 | + $this->approximateLineInfo = false; |
|
| 99 | 99 | |
| 100 | 100 | |
| 101 | - if ( $this->input instanceof TokenStream ) { |
|
| 101 | + if ($this->input instanceof TokenStream) { |
|
| 102 | 102 | $this->token = $input->LT(1); |
| 103 | 103 | $this->line = $this->token->getLine(); |
| 104 | 104 | $this->charPositionInLine = $this->token->getCharPositionInLine(); |
| 105 | 105 | } |
| 106 | - if ( $this->input instanceof TreeNodeStream ) { |
|
| 106 | + if ($this->input instanceof TreeNodeStream) { |
|
| 107 | 107 | $this->extractInformationFromTreeNodeStream($input); |
| 108 | 108 | } |
| 109 | - else if ( $input instanceof CharStream ) { |
|
| 109 | + else if ($input instanceof CharStream) { |
|
| 110 | 110 | $this->c = $input->LA(1); |
| 111 | 111 | $this->line = $input->getLine(); |
| 112 | 112 | $this->charPositionInLine = $input->getCharPositionInLine(); |
@@ -121,15 +121,15 @@ discard block |
||
| 121 | 121 | $this->node = $nodes->LT(1); |
| 122 | 122 | $adaptor = $nodes->getTreeAdaptor(); |
| 123 | 123 | $payload = $adaptor->getToken($this->node); |
| 124 | - if ( $payload!=null ) { |
|
| 124 | + if ($payload != null) { |
|
| 125 | 125 | $this->token = $payload; |
| 126 | - if ( $payload->getLine()<= 0 ) { |
|
| 126 | + if ($payload->getLine() <= 0) { |
|
| 127 | 127 | // imaginary node; no line/pos info; scan backwards |
| 128 | 128 | $i = -1; |
| 129 | 129 | $priorNode = $nodes->LT($i); |
| 130 | - while ( $priorNode!=null ) { |
|
| 130 | + while ($priorNode != null) { |
|
| 131 | 131 | $priorPayload = $adaptor->getToken($priorNode); |
| 132 | - if ( $priorPayload!=null && $priorPayload->getLine()>0 ) { |
|
| 132 | + if ($priorPayload != null && $priorPayload->getLine() > 0) { |
|
| 133 | 133 | // we found the most recent real line / pos info |
| 134 | 134 | $this->line = $priorPayload->getLine(); |
| 135 | 135 | $this->charPositionInLine = $priorPayload->getCharPositionInLine(); |
@@ -145,10 +145,10 @@ discard block |
||
| 145 | 145 | $this->charPositionInLine = $payload->getCharPositionInLine(); |
| 146 | 146 | } |
| 147 | 147 | } |
| 148 | - else if ( $this->node instanceof Tree) { |
|
| 148 | + else if ($this->node instanceof Tree) { |
|
| 149 | 149 | $this->line = $this->node->getLine(); |
| 150 | 150 | $this->charPositionInLine = $this->node->getCharPositionInLine(); |
| 151 | - if ( $this->node instanceof CommonTree) { |
|
| 151 | + if ($this->node instanceof CommonTree) { |
|
| 152 | 152 | $this->token = $this->node->token; |
| 153 | 153 | } |
| 154 | 154 | } |
@@ -161,10 +161,10 @@ discard block |
||
| 161 | 161 | |
| 162 | 162 | /** Return the token type or char of the unexpected input element */ |
| 163 | 163 | public function getUnexpectedType() { |
| 164 | - if ( $this->input instanceof TokenStream ) { |
|
| 164 | + if ($this->input instanceof TokenStream) { |
|
| 165 | 165 | return $this->token->getType(); |
| 166 | 166 | } |
| 167 | - else if ( $this->input instanceof TreeNodeStream ) { |
|
| 167 | + else if ($this->input instanceof TreeNodeStream) { |
|
| 168 | 168 | $nodes = $this->input; |
| 169 | 169 | $adaptor = $nodes->getTreeAdaptor(); |
| 170 | 170 | return $adaptor->getType($this->node); |
@@ -105,13 +105,11 @@ discard block |
||
| 105 | 105 | } |
| 106 | 106 | if ( $this->input instanceof TreeNodeStream ) { |
| 107 | 107 | $this->extractInformationFromTreeNodeStream($input); |
| 108 | - } |
|
| 109 | - else if ( $input instanceof CharStream ) { |
|
| 108 | + } else if ( $input instanceof CharStream ) { |
|
| 110 | 109 | $this->c = $input->LA(1); |
| 111 | 110 | $this->line = $input->getLine(); |
| 112 | 111 | $this->charPositionInLine = $input->getCharPositionInLine(); |
| 113 | - } |
|
| 114 | - else { |
|
| 112 | + } else { |
|
| 115 | 113 | $this->c = $input->LA(1); |
| 116 | 114 | } |
| 117 | 115 | } |
@@ -139,20 +137,17 @@ discard block |
||
| 139 | 137 | --$i; |
| 140 | 138 | $priorNode = $nodes->LT($i); |
| 141 | 139 | } |
| 142 | - } |
|
| 143 | - else { // node created from real token |
|
| 140 | + } else { // node created from real token |
|
| 144 | 141 | $this->line = $payload->getLine(); |
| 145 | 142 | $this->charPositionInLine = $payload->getCharPositionInLine(); |
| 146 | 143 | } |
| 147 | - } |
|
| 148 | - else if ( $this->node instanceof Tree) { |
|
| 144 | + } else if ( $this->node instanceof Tree) { |
|
| 149 | 145 | $this->line = $this->node->getLine(); |
| 150 | 146 | $this->charPositionInLine = $this->node->getCharPositionInLine(); |
| 151 | 147 | if ( $this->node instanceof CommonTree) { |
| 152 | 148 | $this->token = $this->node->token; |
| 153 | 149 | } |
| 154 | - } |
|
| 155 | - else { |
|
| 150 | + } else { |
|
| 156 | 151 | $type = $adaptor->getType($this->node); |
| 157 | 152 | $text = $adaptor->getText($this->node); |
| 158 | 153 | $this->token = CommonToken::forTypeAndText($type, $text); |
@@ -163,13 +158,11 @@ discard block |
||
| 163 | 158 | public function getUnexpectedType() { |
| 164 | 159 | if ( $this->input instanceof TokenStream ) { |
| 165 | 160 | return $this->token->getType(); |
| 166 | - } |
|
| 167 | - else if ( $this->input instanceof TreeNodeStream ) { |
|
| 161 | + } else if ( $this->input instanceof TreeNodeStream ) { |
|
| 168 | 162 | $nodes = $this->input; |
| 169 | 163 | $adaptor = $nodes->getTreeAdaptor(); |
| 170 | 164 | return $adaptor->getType($this->node); |
| 171 | - } |
|
| 172 | - else { |
|
| 165 | + } else { |
|
| 173 | 166 | return $this->c; |
| 174 | 167 | } |
| 175 | 168 | } |
@@ -62,11 +62,11 @@ discard block |
||
| 62 | 62 | */ |
| 63 | 63 | protected $p = -1; |
| 64 | 64 | |
| 65 | - public function __construct($tokenSource, $channel=null) { |
|
| 65 | + public function __construct($tokenSource, $channel = null) { |
|
| 66 | 66 | $this->channel = TokenConst::$DEFAULT_CHANNEL; |
| 67 | 67 | $this->tokens = array(); |
| 68 | 68 | $this->tokenSource = $tokenSource; |
| 69 | - if($channel != null){ |
|
| 69 | + if ($channel != null) { |
|
| 70 | 70 | $this->channel = $channel; |
| 71 | 71 | } |
| 72 | 72 | } |
@@ -86,24 +86,24 @@ discard block |
||
| 86 | 86 | protected function fillBuffer() { |
| 87 | 87 | $index = 0; |
| 88 | 88 | $t = $this->tokenSource->nextToken(); |
| 89 | - while ( $t!=null && $t->getType()!=CharStreamConst::$EOF ) { |
|
| 89 | + while ($t != null && $t->getType() != CharStreamConst::$EOF) { |
|
| 90 | 90 | $discard = false; |
| 91 | 91 | // is there a channel override for token type? |
| 92 | - if ( $this->channelOverrideMap!=null ) { |
|
| 92 | + if ($this->channelOverrideMap != null) { |
|
| 93 | 93 | $channelI = $this->channelOverrideMap[$t->getType()]; |
| 94 | - if ( $channelI!=null ) { |
|
| 94 | + if ($channelI != null) { |
|
| 95 | 95 | $t->setChannel($channelI); |
| 96 | 96 | } |
| 97 | 97 | } |
| 98 | - if ( $this->discardSet!=null && |
|
| 98 | + if ($this->discardSet != null && |
|
| 99 | 99 | $this->discardSet->contains($t->getType())) |
| 100 | 100 | { |
| 101 | 101 | $discard = true; |
| 102 | 102 | } |
| 103 | - else if ( $this->discardOffChannelTokens && $t->getChannel()!=$this->channel ) { |
|
| 103 | + else if ($this->discardOffChannelTokens && $t->getChannel() != $this->channel) { |
|
| 104 | 104 | $discard = true; |
| 105 | 105 | } |
| 106 | - if ( !$discard ) { |
|
| 106 | + if (!$discard) { |
|
| 107 | 107 | $t->setTokenIndex($index); |
| 108 | 108 | $this->tokens[] = $t; |
| 109 | 109 | $index++; |
@@ -123,7 +123,7 @@ discard block |
||
| 123 | 123 | * Walk past any token not on the channel the parser is listening to. |
| 124 | 124 | */ |
| 125 | 125 | public function consume() { |
| 126 | - if ( $this->p<sizeof($this->tokens)) { |
|
| 126 | + if ($this->p < sizeof($this->tokens)) { |
|
| 127 | 127 | $this->p++; |
| 128 | 128 | $this->p = $this->skipOffTokenChannels($this->p); // leave p on valid token |
| 129 | 129 | } |
@@ -134,14 +134,14 @@ discard block |
||
| 134 | 134 | */ |
| 135 | 135 | protected function skipOffTokenChannels($i) { |
| 136 | 136 | $n = sizeof($this->tokens); |
| 137 | - while ( $i<$n && $this->tokens[$i]->getChannel()!=$this->channel ) { |
|
| 137 | + while ($i < $n && $this->tokens[$i]->getChannel() != $this->channel) { |
|
| 138 | 138 | $i++; |
| 139 | 139 | } |
| 140 | 140 | return $i; |
| 141 | 141 | } |
| 142 | 142 | |
| 143 | 143 | protected function skipOffTokenChannelsReverse($i) { |
| 144 | - while ( $i>=0 && $this->tokens[$i]->getChannel()!=$this->channel) { |
|
| 144 | + while ($i >= 0 && $this->tokens[$i]->getChannel() != $this->channel) { |
|
| 145 | 145 | $i--; |
| 146 | 146 | } |
| 147 | 147 | return $i; |
@@ -154,17 +154,17 @@ discard block |
||
| 154 | 154 | * channel. |
| 155 | 155 | */ |
| 156 | 156 | public function setTokenTypeChannel($ttype, $channel) { |
| 157 | - if ( $this->channelOverrideMap==null ) { |
|
| 157 | + if ($this->channelOverrideMap == null) { |
|
| 158 | 158 | $this->channelOverrideMap = array(); |
| 159 | 159 | } |
| 160 | 160 | $this->channelOverrideMap[$ttype] = $channel; |
| 161 | 161 | } |
| 162 | 162 | |
| 163 | 163 | public function discardTokenType($ttype) { |
| 164 | - if ( $this->discardSet==null ) { |
|
| 164 | + if ($this->discardSet == null) { |
|
| 165 | 165 | $this->discardSet = new Set(); |
| 166 | 166 | } |
| 167 | - $this->discardSet.add($ttype); |
|
| 167 | + $this->discardSet . add($ttype); |
|
| 168 | 168 | } |
| 169 | 169 | |
| 170 | 170 | public function discardOffChannelTokens($discardOffChannelTokens) { |
@@ -172,7 +172,7 @@ discard block |
||
| 172 | 172 | } |
| 173 | 173 | |
| 174 | 174 | public function getTokens() { |
| 175 | - if ( $this->p == -1 ) { |
|
| 175 | + if ($this->p == -1) { |
|
| 176 | 176 | $this->fillBuffer(); |
| 177 | 177 | } |
| 178 | 178 | return $this->tokens; |
@@ -187,35 +187,35 @@ discard block |
||
| 187 | 187 | * method looks at both on and off channel tokens. |
| 188 | 188 | */ |
| 189 | 189 | public function getTokensOfTypeInSet($start, $stop, $types) { |
| 190 | - if ( $p == -1 ) { |
|
| 190 | + if ($p == -1) { |
|
| 191 | 191 | fillBuffer(); |
| 192 | 192 | } |
| 193 | - if ( $stop>=sizeof($this->tokens)) { |
|
| 194 | - $stop=sizeof($this->tokens) - 1; |
|
| 193 | + if ($stop >= sizeof($this->tokens)) { |
|
| 194 | + $stop = sizeof($this->tokens) - 1; |
|
| 195 | 195 | } |
| 196 | - if ( $start<0 ) { |
|
| 197 | - $start=0; |
|
| 196 | + if ($start < 0) { |
|
| 197 | + $start = 0; |
|
| 198 | 198 | } |
| 199 | - if ( $start>$stop ) { |
|
| 199 | + if ($start > $stop) { |
|
| 200 | 200 | return null; |
| 201 | 201 | } |
| 202 | 202 | |
| 203 | 203 | // list = tokens[start:stop]:{Token t, t.getType() in types} |
| 204 | 204 | $filteredTokens = array(); |
| 205 | - for ($i=$start; $i<=$stop; $i++) { |
|
| 205 | + for ($i = $start; $i <= $stop; $i++) { |
|
| 206 | 206 | $t = $this->tokens[$i]; |
| 207 | - if ( $types==null || $types->member($t->getType())) { |
|
| 207 | + if ($types == null || $types->member($t->getType())) { |
|
| 208 | 208 | $filteredTokens->add($t); |
| 209 | 209 | } |
| 210 | 210 | } |
| 211 | - if ( sizeof($filteredTokens)==0 ) { |
|
| 211 | + if (sizeof($filteredTokens) == 0) { |
|
| 212 | 212 | $filteredTokens = null; |
| 213 | 213 | } |
| 214 | 214 | return $filteredTokens; |
| 215 | 215 | } |
| 216 | 216 | |
| 217 | 217 | public function getTokensOfTypeInArray($start, $stop, $types) { |
| 218 | - return $this->getTokens($start, $stop,new Set(types)); |
|
| 218 | + return $this->getTokens($start, $stop, new Set(types)); |
|
| 219 | 219 | } |
| 220 | 220 | |
| 221 | 221 | public function getTokensofType($start, $stop, $ttype) { |
@@ -226,29 +226,29 @@ discard block |
||
| 226 | 226 | * first symbol of lookahead. |
| 227 | 227 | */ |
| 228 | 228 | public function LT($k) { |
| 229 | - if ( $this->p == -1 ) { |
|
| 229 | + if ($this->p == -1) { |
|
| 230 | 230 | $this->fillBuffer(); |
| 231 | 231 | } |
| 232 | - if ( $k==0 ) { |
|
| 232 | + if ($k == 0) { |
|
| 233 | 233 | return null; |
| 234 | 234 | } |
| 235 | - if ( $k<0 ) { |
|
| 235 | + if ($k < 0) { |
|
| 236 | 236 | return $this->LB(-$k); |
| 237 | 237 | } |
| 238 | 238 | //System.out.print("LT(p="+p+","+k+")="); |
| 239 | - if ( ($this->p+$k-1) >= sizeof($this->tokens)) { |
|
| 239 | + if (($this->p + $k - 1) >= sizeof($this->tokens)) { |
|
| 240 | 240 | return TokenConst::$EOF_TOKEN; |
| 241 | 241 | } |
| 242 | 242 | //System.out.println(tokens.get(p+k-1)); |
| 243 | 243 | $i = $this->p; |
| 244 | 244 | $n = 1; |
| 245 | 245 | // find k good tokens |
| 246 | - while ( $n<$k ) { |
|
| 246 | + while ($n < $k) { |
|
| 247 | 247 | // skip off-channel tokens |
| 248 | - $i = $this->skipOffTokenChannels($i+1); // leave p on valid token |
|
| 248 | + $i = $this->skipOffTokenChannels($i + 1); // leave p on valid token |
|
| 249 | 249 | $n++; |
| 250 | 250 | } |
| 251 | - if ( $i>=sizeof($this->tokens)) { |
|
| 251 | + if ($i >= sizeof($this->tokens)) { |
|
| 252 | 252 | return TokenConst::$EOF_TOKEN; |
| 253 | 253 | } |
| 254 | 254 | return $this->tokens[$i]; |
@@ -257,13 +257,13 @@ discard block |
||
| 257 | 257 | /** Look backwards k tokens on-channel tokens */ |
| 258 | 258 | protected function LB($k) { |
| 259 | 259 | //System.out.print("LB(p="+p+","+k+") "); |
| 260 | - if ( $this->p == -1 ) { |
|
| 260 | + if ($this->p == -1) { |
|
| 261 | 261 | $this->fillBuffer(); |
| 262 | 262 | } |
| 263 | - if ( $k==0 ) { |
|
| 263 | + if ($k == 0) { |
|
| 264 | 264 | return null; |
| 265 | 265 | } |
| 266 | - if ( ($this->p-$k)<0 ) { |
|
| 266 | + if (($this->p - $k) < 0) { |
|
| 267 | 267 | return null; |
| 268 | 268 | } |
| 269 | 269 | |
@@ -271,12 +271,12 @@ discard block |
||
| 271 | 271 | $i = $this->p; |
| 272 | 272 | $n = 1; |
| 273 | 273 | // find k good tokens looking backwards |
| 274 | - while ( $n<=$k ) { |
|
| 274 | + while ($n <= $k) { |
|
| 275 | 275 | // skip off-channel tokens |
| 276 | - $i = $this->skipOffTokenChannelsReverse($i-1); // leave p on valid token |
|
| 276 | + $i = $this->skipOffTokenChannelsReverse($i - 1); // leave p on valid token |
|
| 277 | 277 | $n++; |
| 278 | 278 | } |
| 279 | - if ( $i<0 ) { |
|
| 279 | + if ($i < 0) { |
|
| 280 | 280 | return null; |
| 281 | 281 | } |
| 282 | 282 | return $this->tokens[$i]; |
@@ -295,7 +295,7 @@ discard block |
||
| 295 | 295 | } |
| 296 | 296 | |
| 297 | 297 | public function mark() { |
| 298 | - if ( $this->p == -1 ) { |
|
| 298 | + if ($this->p == -1) { |
|
| 299 | 299 | $this->fillBuffer(); |
| 300 | 300 | } |
| 301 | 301 | $this->lastMarker = $this->index(); |
@@ -315,7 +315,7 @@ discard block |
||
| 315 | 315 | } |
| 316 | 316 | |
| 317 | 317 | public function rewind($marker = null) { |
| 318 | - if($marker===null){ |
|
| 318 | + if ($marker === null) { |
|
| 319 | 319 | $marker = $this->lastmarker; |
| 320 | 320 | } |
| 321 | 321 | $this->seek($marker); |
@@ -340,38 +340,38 @@ discard block |
||
| 340 | 340 | } |
| 341 | 341 | |
| 342 | 342 | public function toString() { |
| 343 | - if ( $this->p == -1 ) { |
|
| 343 | + if ($this->p == -1) { |
|
| 344 | 344 | $this->fillBuffer(); |
| 345 | 345 | } |
| 346 | - return $this->toStringBetween(0, sizeof($this->tokens)-1); |
|
| 346 | + return $this->toStringBetween(0, sizeof($this->tokens) - 1); |
|
| 347 | 347 | } |
| 348 | 348 | |
| 349 | 349 | public function toStringBetween($start, $stop) { |
| 350 | - if ( $start<0 || $stop<0 ) { |
|
| 350 | + if ($start < 0 || $stop < 0) { |
|
| 351 | 351 | return null; |
| 352 | 352 | } |
| 353 | - if ( $this->p == -1 ) { |
|
| 353 | + if ($this->p == -1) { |
|
| 354 | 354 | $this->fillBuffer(); |
| 355 | 355 | } |
| 356 | - if ( $stop>=sizeof($this->tokens)) { |
|
| 357 | - $stop = sizeof($this->tokens)-1; |
|
| 356 | + if ($stop >= sizeof($this->tokens)) { |
|
| 357 | + $stop = sizeof($this->tokens) - 1; |
|
| 358 | 358 | } |
| 359 | 359 | $buf = ""; |
| 360 | 360 | for ($i = $start; $i <= $stop; $i++) { |
| 361 | 361 | $t = $this->tokens[$i]; |
| 362 | - $buf.=$t->getText(); |
|
| 362 | + $buf .= $t->getText(); |
|
| 363 | 363 | } |
| 364 | 364 | return $buf; |
| 365 | 365 | } |
| 366 | 366 | |
| 367 | 367 | public function toStringBetweenTokens($start, $stop) { |
| 368 | - if ( $start!=null && $stop!=null ) { |
|
| 368 | + if ($start != null && $stop != null) { |
|
| 369 | 369 | return toString($this->start->getTokenIndex(), $this->stop->getTokenIndex()); |
| 370 | 370 | } |
| 371 | 371 | return null; |
| 372 | 372 | } |
| 373 | 373 | |
| 374 | - public function __toString(){ |
|
| 374 | + public function __toString() { |
|
| 375 | 375 | return $this->toString(); |
| 376 | 376 | } |
| 377 | 377 | } |
@@ -99,8 +99,7 @@ |
||
| 99 | 99 | $this->discardSet->contains($t->getType())) |
| 100 | 100 | { |
| 101 | 101 | $discard = true; |
| 102 | - } |
|
| 103 | - else if ( $this->discardOffChannelTokens && $t->getChannel()!=$this->channel ) { |
|
| 102 | + } else if ( $this->discardOffChannelTokens && $t->getChannel()!=$this->channel ) { |
|
| 104 | 103 | $discard = true; |
| 105 | 104 | } |
| 106 | 105 | if ( !$discard ) { |
@@ -28,7 +28,7 @@ discard block |
||
| 28 | 28 | */ |
| 29 | 29 | |
| 30 | 30 | class CommonTokenStream implements TokenStream { |
| 31 | - protected $tokenSource; |
|
| 31 | + protected $tokenSource; |
|
| 32 | 32 | |
| 33 | 33 | /** Record every single token pulled from the source so we can reproduce |
| 34 | 34 | * chunks of it later. |
@@ -51,9 +51,9 @@ discard block |
||
| 51 | 51 | protected $lastMarker = 0; |
| 52 | 52 | |
| 53 | 53 | /** The index into the tokens list of the current token (next token |
| 54 | - * to consume). p==-1 indicates that the tokens list is empty |
|
| 55 | - */ |
|
| 56 | - protected $p = -1; |
|
| 54 | + * to consume). p==-1 indicates that the tokens list is empty |
|
| 55 | + */ |
|
| 56 | + protected $p = -1; |
|
| 57 | 57 | |
| 58 | 58 | public function __construct($tokenSource, $channel=null) { |
| 59 | 59 | $this->channel = TokenConst::$DEFAULT_CHANNEL; |
@@ -106,7 +106,7 @@ discard block |
||
| 106 | 106 | // leave p pointing at first token on channel |
| 107 | 107 | $this->p = 0; |
| 108 | 108 | $this->p = $this->skipOffTokenChannels($this->p); |
| 109 | - } |
|
| 109 | + } |
|
| 110 | 110 | |
| 111 | 111 | /** Move the input pointer to the next incoming token. The stream |
| 112 | 112 | * must become active with LT(1) available. consume() simply |
@@ -117,10 +117,10 @@ discard block |
||
| 117 | 117 | */ |
| 118 | 118 | public function consume() { |
| 119 | 119 | if ( $this->p<sizeof($this->tokens)) { |
| 120 | - $this->p++; |
|
| 120 | + $this->p++; |
|
| 121 | 121 | $this->p = $this->skipOffTokenChannels($this->p); // leave p on valid token |
| 122 | - } |
|
| 123 | - } |
|
| 122 | + } |
|
| 123 | + } |
|
| 124 | 124 | |
| 125 | 125 | /** Given a starting index, return the index of the first on-channel |
| 126 | 126 | * token. |
@@ -150,14 +150,14 @@ discard block |
||
| 150 | 150 | if ( $this->channelOverrideMap==null ) { |
| 151 | 151 | $this->channelOverrideMap = array(); |
| 152 | 152 | } |
| 153 | - $this->channelOverrideMap[$ttype] = $channel; |
|
| 153 | + $this->channelOverrideMap[$ttype] = $channel; |
|
| 154 | 154 | } |
| 155 | 155 | |
| 156 | 156 | public function discardTokenType($ttype) { |
| 157 | 157 | if ( $this->discardSet==null ) { |
| 158 | 158 | $this->discardSet = new Set(); |
| 159 | 159 | } |
| 160 | - $this->discardSet.add($ttype); |
|
| 160 | + $this->discardSet.add($ttype); |
|
| 161 | 161 | } |
| 162 | 162 | |
| 163 | 163 | public function discardOffChannelTokens($discardOffChannelTokens) { |
@@ -244,8 +244,8 @@ discard block |
||
| 244 | 244 | if ( $i>=sizeof($this->tokens)) { |
| 245 | 245 | return TokenConst::$EOF_TOKEN; |
| 246 | 246 | } |
| 247 | - return $this->tokens[$i]; |
|
| 248 | - } |
|
| 247 | + return $this->tokens[$i]; |
|
| 248 | + } |
|
| 249 | 249 | |
| 250 | 250 | /** Look backwards k tokens on-channel tokens */ |
| 251 | 251 | protected function LB($k) { |
@@ -282,12 +282,12 @@ discard block |
||
| 282 | 282 | return $this->tokens[$i]; |
| 283 | 283 | } |
| 284 | 284 | |
| 285 | - public function LA($i) { |
|
| 285 | + public function LA($i) { |
|
| 286 | 286 | $lt = $this->LT($i); |
| 287 | - return $this->LT($i)->getType(); |
|
| 288 | - } |
|
| 287 | + return $this->LT($i)->getType(); |
|
| 288 | + } |
|
| 289 | 289 | |
| 290 | - public function mark() { |
|
| 290 | + public function mark() { |
|
| 291 | 291 | if ( $this->p == -1 ) { |
| 292 | 292 | $this->fillBuffer(); |
| 293 | 293 | } |
@@ -303,9 +303,9 @@ discard block |
||
| 303 | 303 | return sizeof($this->tokens); |
| 304 | 304 | } |
| 305 | 305 | |
| 306 | - public function index() { |
|
| 307 | - return $this->p; |
|
| 308 | - } |
|
| 306 | + public function index() { |
|
| 307 | + return $this->p; |
|
| 308 | + } |
|
| 309 | 309 | |
| 310 | 310 | public function rewind($marker = null) { |
| 311 | 311 | if($marker===null){ |
@@ -339,15 +339,15 @@ |
||
| 339 | 339 | } |
| 340 | 340 | if ($this->options['autooptimize']) { |
| 341 | 341 | switch($this->db->phptype) { |
| 342 | - case 'mysql': |
|
| 343 | - $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); |
|
| 344 | - break; |
|
| 345 | - case 'pgsql': |
|
| 346 | - $query = sprintf("VACUUM %s", $this->options['table']); |
|
| 347 | - break; |
|
| 348 | - default: |
|
| 349 | - $query = null; |
|
| 350 | - break; |
|
| 342 | + case 'mysql': |
|
| 343 | + $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); |
|
| 344 | + break; |
|
| 345 | + case 'pgsql': |
|
| 346 | + $query = sprintf("VACUUM %s", $this->options['table']); |
|
| 347 | + break; |
|
| 348 | + default: |
|
| 349 | + $query = null; |
|
| 350 | + break; |
|
| 351 | 351 | } |
| 352 | 352 | if (isset($query)) { |
| 353 | 353 | $result = $this->db->query($query); |
@@ -338,7 +338,7 @@ |
||
| 338 | 338 | return false; |
| 339 | 339 | } |
| 340 | 340 | if ($this->options['autooptimize']) { |
| 341 | - switch($this->db->phptype) { |
|
| 341 | + switch ($this->db->phptype) { |
|
| 342 | 342 | case 'mysql': |
| 343 | 343 | $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); |
| 344 | 344 | break; |
@@ -59,306 +59,306 @@ |
||
| 59 | 59 | */ |
| 60 | 60 | class HTTP_Session_Container_MDB2 extends HTTP_Session_Container |
| 61 | 61 | { |
| 62 | - /** |
|
| 63 | - * MDB2 connection object |
|
| 64 | - * |
|
| 65 | - * @var object MDB2 |
|
| 66 | - * @access private |
|
| 67 | - */ |
|
| 68 | - public $db = null; |
|
| 62 | + /** |
|
| 63 | + * MDB2 connection object |
|
| 64 | + * |
|
| 65 | + * @var object MDB2 |
|
| 66 | + * @access private |
|
| 67 | + */ |
|
| 68 | + public $db = null; |
|
| 69 | 69 | |
| 70 | - /** |
|
| 71 | - * Session data cache id |
|
| 72 | - * |
|
| 73 | - * @var mixed |
|
| 74 | - * @access private |
|
| 75 | - */ |
|
| 76 | - public $crc = false; |
|
| 70 | + /** |
|
| 71 | + * Session data cache id |
|
| 72 | + * |
|
| 73 | + * @var mixed |
|
| 74 | + * @access private |
|
| 75 | + */ |
|
| 76 | + public $crc = false; |
|
| 77 | 77 | |
| 78 | - /** |
|
| 79 | - * Constructor method |
|
| 80 | - * |
|
| 81 | - * $options is an array with the options.<br> |
|
| 82 | - * The options are: |
|
| 83 | - * <ul> |
|
| 84 | - * <li>'dsn' - The DSN string</li> |
|
| 85 | - * <li>'table' - Table with session data, default is 'sessiondata'</li> |
|
| 86 | - * <li>'autooptimize' - Boolean, 'true' to optimize |
|
| 87 | - * the table on garbage collection, default is 'false'.</li> |
|
| 88 | - * </ul> |
|
| 89 | - * |
|
| 90 | - * @param array $options Options |
|
| 91 | - * |
|
| 92 | - * @access public |
|
| 93 | - * @return void |
|
| 94 | - */ |
|
| 95 | - public function HTTP_Session_Container_MDB2($options) |
|
| 96 | - { |
|
| 97 | - $this->_setDefaults(); |
|
| 98 | - if (is_array($options)) { |
|
| 99 | - $this->_parseOptions($options); |
|
| 100 | - } else { |
|
| 101 | - $this->options['dsn'] = $options; |
|
| 102 | - } |
|
| 103 | - } |
|
| 78 | + /** |
|
| 79 | + * Constructor method |
|
| 80 | + * |
|
| 81 | + * $options is an array with the options.<br> |
|
| 82 | + * The options are: |
|
| 83 | + * <ul> |
|
| 84 | + * <li>'dsn' - The DSN string</li> |
|
| 85 | + * <li>'table' - Table with session data, default is 'sessiondata'</li> |
|
| 86 | + * <li>'autooptimize' - Boolean, 'true' to optimize |
|
| 87 | + * the table on garbage collection, default is 'false'.</li> |
|
| 88 | + * </ul> |
|
| 89 | + * |
|
| 90 | + * @param array $options Options |
|
| 91 | + * |
|
| 92 | + * @access public |
|
| 93 | + * @return void |
|
| 94 | + */ |
|
| 95 | + public function HTTP_Session_Container_MDB2($options) |
|
| 96 | + { |
|
| 97 | + $this->_setDefaults(); |
|
| 98 | + if (is_array($options)) { |
|
| 99 | + $this->_parseOptions($options); |
|
| 100 | + } else { |
|
| 101 | + $this->options['dsn'] = $options; |
|
| 102 | + } |
|
| 103 | + } |
|
| 104 | 104 | |
| 105 | - /** |
|
| 106 | - * Connect to database by using the given DSN string |
|
| 107 | - * |
|
| 108 | - * @param string $dsn DSN string |
|
| 109 | - * |
|
| 110 | - * @access private |
|
| 111 | - * @return mixed Object on error, otherwise bool |
|
| 112 | - */ |
|
| 113 | - public function _connect($dsn) |
|
| 114 | - { |
|
| 115 | - if (is_string($dsn) || is_array($dsn)) { |
|
| 116 | - $this->db = MDB2::connect($dsn); |
|
| 117 | - } else if (is_object($dsn) && is_a($dsn, 'MDB2_Driver_Common')) { |
|
| 118 | - $this->db = $dsn; |
|
| 119 | - } else if (is_object($dsn) && MDB2::isError($dsn)) { |
|
| 120 | - return $dsn; |
|
| 121 | - } else { |
|
| 122 | - return new PEAR_Error("The given dsn was not valid in file " . __FILE__ |
|
| 123 | - . " at line " . __LINE__, |
|
| 124 | - 41, |
|
| 125 | - PEAR_ERROR_RETURN, |
|
| 126 | - null, |
|
| 127 | - null |
|
| 128 | - ); |
|
| 105 | + /** |
|
| 106 | + * Connect to database by using the given DSN string |
|
| 107 | + * |
|
| 108 | + * @param string $dsn DSN string |
|
| 109 | + * |
|
| 110 | + * @access private |
|
| 111 | + * @return mixed Object on error, otherwise bool |
|
| 112 | + */ |
|
| 113 | + public function _connect($dsn) |
|
| 114 | + { |
|
| 115 | + if (is_string($dsn) || is_array($dsn)) { |
|
| 116 | + $this->db = MDB2::connect($dsn); |
|
| 117 | + } else if (is_object($dsn) && is_a($dsn, 'MDB2_Driver_Common')) { |
|
| 118 | + $this->db = $dsn; |
|
| 119 | + } else if (is_object($dsn) && MDB2::isError($dsn)) { |
|
| 120 | + return $dsn; |
|
| 121 | + } else { |
|
| 122 | + return new PEAR_Error("The given dsn was not valid in file " . __FILE__ |
|
| 123 | + . " at line " . __LINE__, |
|
| 124 | + 41, |
|
| 125 | + PEAR_ERROR_RETURN, |
|
| 126 | + null, |
|
| 127 | + null |
|
| 128 | + ); |
|
| 129 | 129 | |
| 130 | - } |
|
| 130 | + } |
|
| 131 | 131 | |
| 132 | - if (MDB2::isError($this->db)) { |
|
| 133 | - return new MDB2_Error($this->db->code, PEAR_ERROR_DIE); |
|
| 134 | - } |
|
| 132 | + if (MDB2::isError($this->db)) { |
|
| 133 | + return new MDB2_Error($this->db->code, PEAR_ERROR_DIE); |
|
| 134 | + } |
|
| 135 | 135 | |
| 136 | - return true; |
|
| 137 | - } |
|
| 136 | + return true; |
|
| 137 | + } |
|
| 138 | 138 | |
| 139 | - /** |
|
| 140 | - * Set some default options |
|
| 141 | - * |
|
| 142 | - * @access private |
|
| 143 | - * @return void |
|
| 144 | - */ |
|
| 145 | - public function _setDefaults() |
|
| 146 | - { |
|
| 147 | - $this->options['dsn'] = null; |
|
| 148 | - $this->options['table'] = 'sessiondata'; |
|
| 149 | - $this->options['autooptimize'] = false; |
|
| 150 | - } |
|
| 139 | + /** |
|
| 140 | + * Set some default options |
|
| 141 | + * |
|
| 142 | + * @access private |
|
| 143 | + * @return void |
|
| 144 | + */ |
|
| 145 | + public function _setDefaults() |
|
| 146 | + { |
|
| 147 | + $this->options['dsn'] = null; |
|
| 148 | + $this->options['table'] = 'sessiondata'; |
|
| 149 | + $this->options['autooptimize'] = false; |
|
| 150 | + } |
|
| 151 | 151 | |
| 152 | - /** |
|
| 153 | - * Establish connection to a database |
|
| 154 | - * |
|
| 155 | - * @param string $save_path Save path |
|
| 156 | - * @param string $session_name Session name |
|
| 157 | - * |
|
| 158 | - * @return bool |
|
| 159 | - */ |
|
| 160 | - public function open($save_path, $session_name) |
|
| 161 | - { |
|
| 162 | - if (MDB2::isError($this->_connect($this->options['dsn']))) { |
|
| 163 | - return false; |
|
| 164 | - } else { |
|
| 165 | - return true; |
|
| 166 | - } |
|
| 167 | - } |
|
| 152 | + /** |
|
| 153 | + * Establish connection to a database |
|
| 154 | + * |
|
| 155 | + * @param string $save_path Save path |
|
| 156 | + * @param string $session_name Session name |
|
| 157 | + * |
|
| 158 | + * @return bool |
|
| 159 | + */ |
|
| 160 | + public function open($save_path, $session_name) |
|
| 161 | + { |
|
| 162 | + if (MDB2::isError($this->_connect($this->options['dsn']))) { |
|
| 163 | + return false; |
|
| 164 | + } else { |
|
| 165 | + return true; |
|
| 166 | + } |
|
| 167 | + } |
|
| 168 | 168 | |
| 169 | - /** |
|
| 170 | - * Free resources |
|
| 171 | - * |
|
| 172 | - * @return bool |
|
| 173 | - */ |
|
| 174 | - public function close() |
|
| 175 | - { |
|
| 176 | - return true; |
|
| 177 | - } |
|
| 169 | + /** |
|
| 170 | + * Free resources |
|
| 171 | + * |
|
| 172 | + * @return bool |
|
| 173 | + */ |
|
| 174 | + public function close() |
|
| 175 | + { |
|
| 176 | + return true; |
|
| 177 | + } |
|
| 178 | 178 | |
| 179 | - /** |
|
| 180 | - * Read session data |
|
| 181 | - * |
|
| 182 | - * @param string $id Session id |
|
| 183 | - * |
|
| 184 | - * @return mixed |
|
| 185 | - */ |
|
| 186 | - public function read($id) |
|
| 187 | - { |
|
| 188 | - $query = sprintf("SELECT data FROM %s WHERE id = %s && expiry >= %d", |
|
| 189 | - $this->options['table'], |
|
| 190 | - $this->db->quote(md5($id), 'text'), |
|
| 191 | - time()); |
|
| 192 | - $result = $this->db->queryOne($query); |
|
| 193 | - if (MDB2::isError($result)) { |
|
| 194 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 195 | - return false; |
|
| 196 | - } |
|
| 197 | - $this->crc = strlen($result) . crc32($result); |
|
| 198 | - return $result; |
|
| 199 | - } |
|
| 179 | + /** |
|
| 180 | + * Read session data |
|
| 181 | + * |
|
| 182 | + * @param string $id Session id |
|
| 183 | + * |
|
| 184 | + * @return mixed |
|
| 185 | + */ |
|
| 186 | + public function read($id) |
|
| 187 | + { |
|
| 188 | + $query = sprintf("SELECT data FROM %s WHERE id = %s && expiry >= %d", |
|
| 189 | + $this->options['table'], |
|
| 190 | + $this->db->quote(md5($id), 'text'), |
|
| 191 | + time()); |
|
| 192 | + $result = $this->db->queryOne($query); |
|
| 193 | + if (MDB2::isError($result)) { |
|
| 194 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 195 | + return false; |
|
| 196 | + } |
|
| 197 | + $this->crc = strlen($result) . crc32($result); |
|
| 198 | + return $result; |
|
| 199 | + } |
|
| 200 | 200 | |
| 201 | - /** |
|
| 202 | - * Write session data |
|
| 203 | - * |
|
| 204 | - * @param string $id Session id |
|
| 205 | - * @param mixed $data Data |
|
| 206 | - * |
|
| 207 | - * @return bool |
|
| 208 | - */ |
|
| 209 | - public function write($id, $data) |
|
| 210 | - { |
|
| 211 | - if ((false !== $this->crc) && |
|
| 212 | - ($this->crc === strlen($data) . crc32($data))) { |
|
| 213 | - // $_SESSION hasn't been touched, no need to update the blob column |
|
| 214 | - $query = sprintf("UPDATE %s SET expiry = %d WHERE id = %s", |
|
| 215 | - $this->options['table'], |
|
| 216 | - time() + ini_get('session.gc_maxlifetime'), |
|
| 217 | - $this->db->quote(md5($id), 'text')); |
|
| 218 | - } else { |
|
| 219 | - // Check if table row already exists |
|
| 220 | - $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", |
|
| 221 | - $this->options['table'], |
|
| 222 | - $this->db->quote(md5($id), 'text')); |
|
| 223 | - $result = $this->db->queryOne($query); |
|
| 224 | - if (MDB2::isError($result)) { |
|
| 225 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 226 | - return false; |
|
| 227 | - } |
|
| 228 | - if (0 == intval($result)) { |
|
| 229 | - // Insert new row into table |
|
| 230 | - $query = sprintf("INSERT INTO %s (id, expiry, data) VALUES (%s, %d, %s)", |
|
| 231 | - $this->options['table'], |
|
| 232 | - $this->db->quote(md5($id), 'text'), |
|
| 233 | - time() + ini_get('session.gc_maxlifetime'), |
|
| 234 | - $this->db->quote($data, 'text')); |
|
| 235 | - } else { |
|
| 236 | - // Update existing row |
|
| 237 | - $query = sprintf("UPDATE %s SET expiry = %d, data = %s WHERE id = %s", |
|
| 238 | - $this->options['table'], |
|
| 239 | - time() + ini_get('session.gc_maxlifetime'), |
|
| 240 | - $this->db->quote($data, 'text'), |
|
| 241 | - $this->db->quote(md5($id), 'text')); |
|
| 242 | - } |
|
| 243 | - } |
|
| 244 | - $result = $this->db->query($query); |
|
| 245 | - if (MDB2::isError($result)) { |
|
| 246 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 247 | - return false; |
|
| 248 | - } |
|
| 201 | + /** |
|
| 202 | + * Write session data |
|
| 203 | + * |
|
| 204 | + * @param string $id Session id |
|
| 205 | + * @param mixed $data Data |
|
| 206 | + * |
|
| 207 | + * @return bool |
|
| 208 | + */ |
|
| 209 | + public function write($id, $data) |
|
| 210 | + { |
|
| 211 | + if ((false !== $this->crc) && |
|
| 212 | + ($this->crc === strlen($data) . crc32($data))) { |
|
| 213 | + // $_SESSION hasn't been touched, no need to update the blob column |
|
| 214 | + $query = sprintf("UPDATE %s SET expiry = %d WHERE id = %s", |
|
| 215 | + $this->options['table'], |
|
| 216 | + time() + ini_get('session.gc_maxlifetime'), |
|
| 217 | + $this->db->quote(md5($id), 'text')); |
|
| 218 | + } else { |
|
| 219 | + // Check if table row already exists |
|
| 220 | + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", |
|
| 221 | + $this->options['table'], |
|
| 222 | + $this->db->quote(md5($id), 'text')); |
|
| 223 | + $result = $this->db->queryOne($query); |
|
| 224 | + if (MDB2::isError($result)) { |
|
| 225 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 226 | + return false; |
|
| 227 | + } |
|
| 228 | + if (0 == intval($result)) { |
|
| 229 | + // Insert new row into table |
|
| 230 | + $query = sprintf("INSERT INTO %s (id, expiry, data) VALUES (%s, %d, %s)", |
|
| 231 | + $this->options['table'], |
|
| 232 | + $this->db->quote(md5($id), 'text'), |
|
| 233 | + time() + ini_get('session.gc_maxlifetime'), |
|
| 234 | + $this->db->quote($data, 'text')); |
|
| 235 | + } else { |
|
| 236 | + // Update existing row |
|
| 237 | + $query = sprintf("UPDATE %s SET expiry = %d, data = %s WHERE id = %s", |
|
| 238 | + $this->options['table'], |
|
| 239 | + time() + ini_get('session.gc_maxlifetime'), |
|
| 240 | + $this->db->quote($data, 'text'), |
|
| 241 | + $this->db->quote(md5($id), 'text')); |
|
| 242 | + } |
|
| 243 | + } |
|
| 244 | + $result = $this->db->query($query); |
|
| 245 | + if (MDB2::isError($result)) { |
|
| 246 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 247 | + return false; |
|
| 248 | + } |
|
| 249 | 249 | |
| 250 | - return true; |
|
| 251 | - } |
|
| 250 | + return true; |
|
| 251 | + } |
|
| 252 | 252 | |
| 253 | - /** |
|
| 254 | - * Destroy session data |
|
| 255 | - * |
|
| 256 | - * @param string $id Session id |
|
| 257 | - * |
|
| 258 | - * @return bool |
|
| 259 | - */ |
|
| 260 | - public function destroy($id) |
|
| 261 | - { |
|
| 262 | - $query = sprintf("DELETE FROM %s WHERE id = %s", |
|
| 263 | - $this->options['table'], |
|
| 264 | - $this->db->quote(md5($id), 'text')); |
|
| 265 | - $result = $this->db->query($query); |
|
| 266 | - if (MDB2::isError($result)) { |
|
| 267 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 268 | - return false; |
|
| 269 | - } |
|
| 253 | + /** |
|
| 254 | + * Destroy session data |
|
| 255 | + * |
|
| 256 | + * @param string $id Session id |
|
| 257 | + * |
|
| 258 | + * @return bool |
|
| 259 | + */ |
|
| 260 | + public function destroy($id) |
|
| 261 | + { |
|
| 262 | + $query = sprintf("DELETE FROM %s WHERE id = %s", |
|
| 263 | + $this->options['table'], |
|
| 264 | + $this->db->quote(md5($id), 'text')); |
|
| 265 | + $result = $this->db->query($query); |
|
| 266 | + if (MDB2::isError($result)) { |
|
| 267 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 268 | + return false; |
|
| 269 | + } |
|
| 270 | 270 | |
| 271 | - return true; |
|
| 272 | - } |
|
| 271 | + return true; |
|
| 272 | + } |
|
| 273 | 273 | |
| 274 | - /** |
|
| 275 | - * Replicate session data to table specified in option 'replicateBeforeDestroy' |
|
| 276 | - * |
|
| 277 | - * @param string $targetTable Table to replicate to |
|
| 278 | - * @param string $id Id of record to replicate |
|
| 279 | - * |
|
| 280 | - * @access private |
|
| 281 | - * @return bool |
|
| 282 | - */ |
|
| 283 | - public function replicate($targetTable, $id = null) |
|
| 284 | - { |
|
| 285 | - if (is_null($id)) { |
|
| 286 | - $id = HTTP_Session::id(); |
|
| 287 | - } |
|
| 274 | + /** |
|
| 275 | + * Replicate session data to table specified in option 'replicateBeforeDestroy' |
|
| 276 | + * |
|
| 277 | + * @param string $targetTable Table to replicate to |
|
| 278 | + * @param string $id Id of record to replicate |
|
| 279 | + * |
|
| 280 | + * @access private |
|
| 281 | + * @return bool |
|
| 282 | + */ |
|
| 283 | + public function replicate($targetTable, $id = null) |
|
| 284 | + { |
|
| 285 | + if (is_null($id)) { |
|
| 286 | + $id = HTTP_Session::id(); |
|
| 287 | + } |
|
| 288 | 288 | |
| 289 | - // Check if table row already exists |
|
| 290 | - $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", |
|
| 291 | - $targetTable, |
|
| 292 | - $this->db->quote(md5($id), 'text')); |
|
| 293 | - $result = $this->db->queryOne($query); |
|
| 294 | - if (MDB2::isError($result)) { |
|
| 295 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 296 | - return false; |
|
| 297 | - } |
|
| 289 | + // Check if table row already exists |
|
| 290 | + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", |
|
| 291 | + $targetTable, |
|
| 292 | + $this->db->quote(md5($id), 'text')); |
|
| 293 | + $result = $this->db->queryOne($query); |
|
| 294 | + if (MDB2::isError($result)) { |
|
| 295 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 296 | + return false; |
|
| 297 | + } |
|
| 298 | 298 | |
| 299 | - // Insert new row into dest table |
|
| 300 | - if (0 == intval($result)) { |
|
| 301 | - $query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = %s", |
|
| 302 | - $targetTable, |
|
| 303 | - $this->options['table'], |
|
| 304 | - $this->db->quote(md5($id), 'text')); |
|
| 299 | + // Insert new row into dest table |
|
| 300 | + if (0 == intval($result)) { |
|
| 301 | + $query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = %s", |
|
| 302 | + $targetTable, |
|
| 303 | + $this->options['table'], |
|
| 304 | + $this->db->quote(md5($id), 'text')); |
|
| 305 | 305 | |
| 306 | - } else { |
|
| 307 | - // Update existing row |
|
| 308 | - $query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id && src.id = %s", |
|
| 309 | - $targetTable, |
|
| 310 | - $this->options['table'], |
|
| 311 | - $this->db->quote(md5($id), 'text')); |
|
| 312 | - } |
|
| 306 | + } else { |
|
| 307 | + // Update existing row |
|
| 308 | + $query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id && src.id = %s", |
|
| 309 | + $targetTable, |
|
| 310 | + $this->options['table'], |
|
| 311 | + $this->db->quote(md5($id), 'text')); |
|
| 312 | + } |
|
| 313 | 313 | |
| 314 | - $result = $this->db->query($query); |
|
| 315 | - if (MDB2::isError($result)) { |
|
| 316 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 317 | - return false; |
|
| 318 | - } |
|
| 314 | + $result = $this->db->query($query); |
|
| 315 | + if (MDB2::isError($result)) { |
|
| 316 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 317 | + return false; |
|
| 318 | + } |
|
| 319 | 319 | |
| 320 | - return true; |
|
| 321 | - } |
|
| 320 | + return true; |
|
| 321 | + } |
|
| 322 | 322 | |
| 323 | - /** |
|
| 324 | - * Garbage collection |
|
| 325 | - * |
|
| 326 | - * @param int $maxlifetime Maximum lifetime |
|
| 327 | - * |
|
| 328 | - * @return bool |
|
| 329 | - */ |
|
| 330 | - public function gc($maxlifetime) |
|
| 331 | - { |
|
| 332 | - $query = sprintf("DELETE FROM %s WHERE expiry < %d", |
|
| 333 | - $this->options['table'], |
|
| 334 | - time()); |
|
| 335 | - $result = $this->db->query($query); |
|
| 336 | - if (MDB2::isError($result)) { |
|
| 337 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 338 | - return false; |
|
| 339 | - } |
|
| 340 | - if ($this->options['autooptimize']) { |
|
| 341 | - switch($this->db->phptype) { |
|
| 342 | - case 'mysql': |
|
| 343 | - $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); |
|
| 344 | - break; |
|
| 345 | - case 'pgsql': |
|
| 346 | - $query = sprintf("VACUUM %s", $this->options['table']); |
|
| 347 | - break; |
|
| 348 | - default: |
|
| 349 | - $query = null; |
|
| 350 | - break; |
|
| 351 | - } |
|
| 352 | - if (isset($query)) { |
|
| 353 | - $result = $this->db->query($query); |
|
| 354 | - if (MDB2::isError($result)) { |
|
| 355 | - $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 356 | - return false; |
|
| 357 | - } |
|
| 358 | - } |
|
| 359 | - } |
|
| 323 | + /** |
|
| 324 | + * Garbage collection |
|
| 325 | + * |
|
| 326 | + * @param int $maxlifetime Maximum lifetime |
|
| 327 | + * |
|
| 328 | + * @return bool |
|
| 329 | + */ |
|
| 330 | + public function gc($maxlifetime) |
|
| 331 | + { |
|
| 332 | + $query = sprintf("DELETE FROM %s WHERE expiry < %d", |
|
| 333 | + $this->options['table'], |
|
| 334 | + time()); |
|
| 335 | + $result = $this->db->query($query); |
|
| 336 | + if (MDB2::isError($result)) { |
|
| 337 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 338 | + return false; |
|
| 339 | + } |
|
| 340 | + if ($this->options['autooptimize']) { |
|
| 341 | + switch($this->db->phptype) { |
|
| 342 | + case 'mysql': |
|
| 343 | + $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); |
|
| 344 | + break; |
|
| 345 | + case 'pgsql': |
|
| 346 | + $query = sprintf("VACUUM %s", $this->options['table']); |
|
| 347 | + break; |
|
| 348 | + default: |
|
| 349 | + $query = null; |
|
| 350 | + break; |
|
| 351 | + } |
|
| 352 | + if (isset($query)) { |
|
| 353 | + $result = $this->db->query($query); |
|
| 354 | + if (MDB2::isError($result)) { |
|
| 355 | + $this->db->raiseError($result->code, PEAR_ERROR_DIE); |
|
| 356 | + return false; |
|
| 357 | + } |
|
| 358 | + } |
|
| 359 | + } |
|
| 360 | 360 | |
| 361 | - return true; |
|
| 362 | - } |
|
| 361 | + return true; |
|
| 362 | + } |
|
| 363 | 363 | } |
| 364 | 364 | ?> |
| 365 | 365 | \ No newline at end of file |
@@ -68,7 +68,7 @@ |
||
| 68 | 68 | |
| 69 | 69 | }); |
| 70 | 70 | |
| 71 | - $server->on('propFind', [$this, 'propFind']); |
|
| 71 | + $server->on('propFind', [$this, 'propFind']); |
|
| 72 | 72 | $server->on('validateTokens', [$this, 'validateTokens']); |
| 73 | 73 | |
| 74 | 74 | } |
@@ -20,258 +20,258 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | class Plugin extends DAV\ServerPlugin { |
| 22 | 22 | |
| 23 | - /** |
|
| 24 | - * Reference to server object |
|
| 25 | - * |
|
| 26 | - * @var DAV\Server |
|
| 27 | - */ |
|
| 28 | - protected $server; |
|
| 29 | - |
|
| 30 | - const SYNCTOKEN_PREFIX = 'http://sabre.io/ns/sync/'; |
|
| 31 | - |
|
| 32 | - /** |
|
| 33 | - * Returns a plugin name. |
|
| 34 | - * |
|
| 35 | - * Using this name other plugins will be able to access other plugins |
|
| 36 | - * using \Sabre\DAV\Server::getPlugin |
|
| 37 | - * |
|
| 38 | - * @return string |
|
| 39 | - */ |
|
| 40 | - public function getPluginName() { |
|
| 41 | - |
|
| 42 | - return 'sync'; |
|
| 43 | - |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - /** |
|
| 47 | - * Initializes the plugin. |
|
| 48 | - * |
|
| 49 | - * This is when the plugin registers it's hooks. |
|
| 50 | - * |
|
| 51 | - * @param DAV\Server $server |
|
| 52 | - * @return void |
|
| 53 | - */ |
|
| 54 | - public function initialize(DAV\Server $server) { |
|
| 55 | - |
|
| 56 | - $this->server = $server; |
|
| 57 | - $server->xml->elementMap['{DAV:}sync-collection'] = 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport'; |
|
| 58 | - |
|
| 59 | - $self = $this; |
|
| 60 | - |
|
| 61 | - $server->on('report', function($reportName, $dom, $uri) use ($self) { |
|
| 62 | - |
|
| 63 | - if ($reportName === '{DAV:}sync-collection') { |
|
| 64 | - $this->server->transactionType = 'report-sync-collection'; |
|
| 65 | - $self->syncCollection($uri, $dom); |
|
| 66 | - return false; |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - }); |
|
| 70 | - |
|
| 71 | - $server->on('propFind', [$this, 'propFind']); |
|
| 72 | - $server->on('validateTokens', [$this, 'validateTokens']); |
|
| 73 | - |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - /** |
|
| 77 | - * Returns a list of reports this plugin supports. |
|
| 78 | - * |
|
| 79 | - * This will be used in the {DAV:}supported-report-set property. |
|
| 80 | - * Note that you still need to subscribe to the 'report' event to actually |
|
| 81 | - * implement them |
|
| 82 | - * |
|
| 83 | - * @param string $uri |
|
| 84 | - * @return array |
|
| 85 | - */ |
|
| 86 | - public function getSupportedReportSet($uri) { |
|
| 87 | - |
|
| 88 | - $node = $this->server->tree->getNodeForPath($uri); |
|
| 89 | - if ($node instanceof ISyncCollection && $node->getSyncToken()) { |
|
| 90 | - return [ |
|
| 91 | - '{DAV:}sync-collection', |
|
| 92 | - ]; |
|
| 93 | - } |
|
| 94 | - |
|
| 95 | - return []; |
|
| 96 | - |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * This method handles the {DAV:}sync-collection HTTP REPORT. |
|
| 102 | - * |
|
| 103 | - * @param string $uri |
|
| 104 | - * @param SyncCollectionReport $report |
|
| 105 | - * @return void |
|
| 106 | - */ |
|
| 107 | - public function syncCollection($uri, SyncCollectionReport $report) { |
|
| 108 | - |
|
| 109 | - // Getting the data |
|
| 110 | - $node = $this->server->tree->getNodeForPath($uri); |
|
| 111 | - if (!$node instanceof ISyncCollection) { |
|
| 112 | - throw new DAV\Exception\ReportNotSupported('The {DAV:}sync-collection REPORT is not supported on this url.'); |
|
| 113 | - } |
|
| 114 | - $token = $node->getSyncToken(); |
|
| 115 | - if (!$token) { |
|
| 116 | - throw new DAV\Exception\ReportNotSupported('No sync information is available at this node'); |
|
| 117 | - } |
|
| 118 | - |
|
| 119 | - $syncToken = $report->syncToken; |
|
| 120 | - if (!is_null($syncToken)) { |
|
| 121 | - // Sync-token must start with our prefix |
|
| 122 | - if (substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { |
|
| 123 | - throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | - $syncToken = substr($syncToken, strlen(self::SYNCTOKEN_PREFIX)); |
|
| 127 | - |
|
| 128 | - } |
|
| 129 | - $changeInfo = $node->getChanges($syncToken, $report->syncLevel, $report->limit); |
|
| 130 | - |
|
| 131 | - if (is_null($changeInfo)) { |
|
| 132 | - |
|
| 133 | - throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); |
|
| 134 | - |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - // Encoding the response |
|
| 138 | - $this->sendSyncCollectionResponse( |
|
| 139 | - $changeInfo['syncToken'], |
|
| 140 | - $uri, |
|
| 141 | - $changeInfo['added'], |
|
| 142 | - $changeInfo['modified'], |
|
| 143 | - $changeInfo['deleted'], |
|
| 144 | - $report->properties |
|
| 145 | - ); |
|
| 146 | - |
|
| 147 | - } |
|
| 148 | - |
|
| 149 | - /** |
|
| 150 | - * Sends the response to a sync-collection request. |
|
| 151 | - * |
|
| 152 | - * @param string $syncToken |
|
| 153 | - * @param string $collectionUrl |
|
| 154 | - * @param array $added |
|
| 155 | - * @param array $modified |
|
| 156 | - * @param array $deleted |
|
| 157 | - * @param array $properties |
|
| 158 | - * @return void |
|
| 159 | - */ |
|
| 160 | - protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) { |
|
| 161 | - |
|
| 162 | - |
|
| 163 | - $fullPaths = []; |
|
| 164 | - |
|
| 165 | - // Pre-fetching children, if this is possible. |
|
| 166 | - foreach (array_merge($added, $modified) as $item) { |
|
| 167 | - $fullPath = $collectionUrl . '/' . $item; |
|
| 168 | - $fullPaths[] = $fullPath; |
|
| 169 | - } |
|
| 170 | - |
|
| 171 | - $responses = []; |
|
| 172 | - foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) { |
|
| 173 | - |
|
| 174 | - // The 'Property_Response' class is responsible for generating a |
|
| 175 | - // single {DAV:}response xml element. |
|
| 176 | - $responses[] = new DAV\Xml\Element\Response($fullPath, $props); |
|
| 177 | - |
|
| 178 | - } |
|
| 179 | - |
|
| 180 | - |
|
| 181 | - |
|
| 182 | - // Deleted items also show up as 'responses'. They have no properties, |
|
| 183 | - // and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'. |
|
| 184 | - foreach ($deleted as $item) { |
|
| 185 | - |
|
| 186 | - $fullPath = $collectionUrl . '/' . $item; |
|
| 187 | - $responses[] = new DAV\Xml\Element\Response($fullPath, [], 404); |
|
| 188 | - |
|
| 189 | - } |
|
| 190 | - $multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX . $syncToken); |
|
| 191 | - |
|
| 192 | - $this->server->httpResponse->setStatus(207); |
|
| 193 | - $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); |
|
| 194 | - $this->server->httpResponse->setBody( |
|
| 195 | - $this->server->xml->write('{DAV:}multistatus', $multiStatus, $this->server->getBaseUri()) |
|
| 196 | - ); |
|
| 197 | - |
|
| 198 | - } |
|
| 199 | - |
|
| 200 | - /** |
|
| 201 | - * This method is triggered whenever properties are requested for a node. |
|
| 202 | - * We intercept this to see if we must return a {DAV:}sync-token. |
|
| 203 | - * |
|
| 204 | - * @param DAV\PropFind $propFind |
|
| 205 | - * @param DAV\INode $node |
|
| 206 | - * @return void |
|
| 207 | - */ |
|
| 208 | - public function propFind(DAV\PropFind $propFind, DAV\INode $node) { |
|
| 209 | - |
|
| 210 | - $propFind->handle('{DAV:}sync-token', function() use ($node) { |
|
| 211 | - if (!$node instanceof ISyncCollection || !$token = $node->getSyncToken()) { |
|
| 212 | - return; |
|
| 213 | - } |
|
| 214 | - return self::SYNCTOKEN_PREFIX . $token; |
|
| 215 | - }); |
|
| 216 | - |
|
| 217 | - } |
|
| 218 | - |
|
| 219 | - /** |
|
| 220 | - * The validateTokens event is triggered before every request. |
|
| 221 | - * |
|
| 222 | - * It's a moment where this plugin can check all the supplied lock tokens |
|
| 223 | - * in the If: header, and check if they are valid. |
|
| 224 | - * |
|
| 225 | - * @param RequestInterface $request |
|
| 226 | - * @param array $conditions |
|
| 227 | - * @return void |
|
| 228 | - */ |
|
| 229 | - public function validateTokens(RequestInterface $request, &$conditions) { |
|
| 230 | - |
|
| 231 | - foreach ($conditions as $kk => $condition) { |
|
| 232 | - |
|
| 233 | - foreach ($condition['tokens'] as $ii => $token) { |
|
| 234 | - |
|
| 235 | - // Sync-tokens must always start with our designated prefix. |
|
| 236 | - if (substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { |
|
| 237 | - continue; |
|
| 238 | - } |
|
| 239 | - |
|
| 240 | - // Checking if the token is a match. |
|
| 241 | - $node = $this->server->tree->getNodeForPath($condition['uri']); |
|
| 242 | - |
|
| 243 | - if ( |
|
| 244 | - $node instanceof ISyncCollection && |
|
| 245 | - $node->getSyncToken() == substr($token['token'], strlen(self::SYNCTOKEN_PREFIX)) |
|
| 246 | - ) { |
|
| 247 | - $conditions[$kk]['tokens'][$ii]['validToken'] = true; |
|
| 248 | - } |
|
| 249 | - |
|
| 250 | - } |
|
| 251 | - |
|
| 252 | - } |
|
| 253 | - |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - /** |
|
| 257 | - * Returns a bunch of meta-data about the plugin. |
|
| 258 | - * |
|
| 259 | - * Providing this information is optional, and is mainly displayed by the |
|
| 260 | - * Browser plugin. |
|
| 261 | - * |
|
| 262 | - * The description key in the returned array may contain html and will not |
|
| 263 | - * be sanitized. |
|
| 264 | - * |
|
| 265 | - * @return array |
|
| 266 | - */ |
|
| 267 | - public function getPluginInfo() { |
|
| 268 | - |
|
| 269 | - return [ |
|
| 270 | - 'name' => $this->getPluginName(), |
|
| 271 | - 'description' => 'Adds support for WebDAV Collection Sync (rfc6578)', |
|
| 272 | - 'link' => 'http://sabre.io/dav/sync/', |
|
| 273 | - ]; |
|
| 274 | - |
|
| 275 | - } |
|
| 23 | + /** |
|
| 24 | + * Reference to server object |
|
| 25 | + * |
|
| 26 | + * @var DAV\Server |
|
| 27 | + */ |
|
| 28 | + protected $server; |
|
| 29 | + |
|
| 30 | + const SYNCTOKEN_PREFIX = 'http://sabre.io/ns/sync/'; |
|
| 31 | + |
|
| 32 | + /** |
|
| 33 | + * Returns a plugin name. |
|
| 34 | + * |
|
| 35 | + * Using this name other plugins will be able to access other plugins |
|
| 36 | + * using \Sabre\DAV\Server::getPlugin |
|
| 37 | + * |
|
| 38 | + * @return string |
|
| 39 | + */ |
|
| 40 | + public function getPluginName() { |
|
| 41 | + |
|
| 42 | + return 'sync'; |
|
| 43 | + |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + /** |
|
| 47 | + * Initializes the plugin. |
|
| 48 | + * |
|
| 49 | + * This is when the plugin registers it's hooks. |
|
| 50 | + * |
|
| 51 | + * @param DAV\Server $server |
|
| 52 | + * @return void |
|
| 53 | + */ |
|
| 54 | + public function initialize(DAV\Server $server) { |
|
| 55 | + |
|
| 56 | + $this->server = $server; |
|
| 57 | + $server->xml->elementMap['{DAV:}sync-collection'] = 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport'; |
|
| 58 | + |
|
| 59 | + $self = $this; |
|
| 60 | + |
|
| 61 | + $server->on('report', function($reportName, $dom, $uri) use ($self) { |
|
| 62 | + |
|
| 63 | + if ($reportName === '{DAV:}sync-collection') { |
|
| 64 | + $this->server->transactionType = 'report-sync-collection'; |
|
| 65 | + $self->syncCollection($uri, $dom); |
|
| 66 | + return false; |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + }); |
|
| 70 | + |
|
| 71 | + $server->on('propFind', [$this, 'propFind']); |
|
| 72 | + $server->on('validateTokens', [$this, 'validateTokens']); |
|
| 73 | + |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + /** |
|
| 77 | + * Returns a list of reports this plugin supports. |
|
| 78 | + * |
|
| 79 | + * This will be used in the {DAV:}supported-report-set property. |
|
| 80 | + * Note that you still need to subscribe to the 'report' event to actually |
|
| 81 | + * implement them |
|
| 82 | + * |
|
| 83 | + * @param string $uri |
|
| 84 | + * @return array |
|
| 85 | + */ |
|
| 86 | + public function getSupportedReportSet($uri) { |
|
| 87 | + |
|
| 88 | + $node = $this->server->tree->getNodeForPath($uri); |
|
| 89 | + if ($node instanceof ISyncCollection && $node->getSyncToken()) { |
|
| 90 | + return [ |
|
| 91 | + '{DAV:}sync-collection', |
|
| 92 | + ]; |
|
| 93 | + } |
|
| 94 | + |
|
| 95 | + return []; |
|
| 96 | + |
|
| 97 | + } |
|
| 98 | + |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * This method handles the {DAV:}sync-collection HTTP REPORT. |
|
| 102 | + * |
|
| 103 | + * @param string $uri |
|
| 104 | + * @param SyncCollectionReport $report |
|
| 105 | + * @return void |
|
| 106 | + */ |
|
| 107 | + public function syncCollection($uri, SyncCollectionReport $report) { |
|
| 108 | + |
|
| 109 | + // Getting the data |
|
| 110 | + $node = $this->server->tree->getNodeForPath($uri); |
|
| 111 | + if (!$node instanceof ISyncCollection) { |
|
| 112 | + throw new DAV\Exception\ReportNotSupported('The {DAV:}sync-collection REPORT is not supported on this url.'); |
|
| 113 | + } |
|
| 114 | + $token = $node->getSyncToken(); |
|
| 115 | + if (!$token) { |
|
| 116 | + throw new DAV\Exception\ReportNotSupported('No sync information is available at this node'); |
|
| 117 | + } |
|
| 118 | + |
|
| 119 | + $syncToken = $report->syncToken; |
|
| 120 | + if (!is_null($syncToken)) { |
|
| 121 | + // Sync-token must start with our prefix |
|
| 122 | + if (substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { |
|
| 123 | + throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + $syncToken = substr($syncToken, strlen(self::SYNCTOKEN_PREFIX)); |
|
| 127 | + |
|
| 128 | + } |
|
| 129 | + $changeInfo = $node->getChanges($syncToken, $report->syncLevel, $report->limit); |
|
| 130 | + |
|
| 131 | + if (is_null($changeInfo)) { |
|
| 132 | + |
|
| 133 | + throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); |
|
| 134 | + |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + // Encoding the response |
|
| 138 | + $this->sendSyncCollectionResponse( |
|
| 139 | + $changeInfo['syncToken'], |
|
| 140 | + $uri, |
|
| 141 | + $changeInfo['added'], |
|
| 142 | + $changeInfo['modified'], |
|
| 143 | + $changeInfo['deleted'], |
|
| 144 | + $report->properties |
|
| 145 | + ); |
|
| 146 | + |
|
| 147 | + } |
|
| 148 | + |
|
| 149 | + /** |
|
| 150 | + * Sends the response to a sync-collection request. |
|
| 151 | + * |
|
| 152 | + * @param string $syncToken |
|
| 153 | + * @param string $collectionUrl |
|
| 154 | + * @param array $added |
|
| 155 | + * @param array $modified |
|
| 156 | + * @param array $deleted |
|
| 157 | + * @param array $properties |
|
| 158 | + * @return void |
|
| 159 | + */ |
|
| 160 | + protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) { |
|
| 161 | + |
|
| 162 | + |
|
| 163 | + $fullPaths = []; |
|
| 164 | + |
|
| 165 | + // Pre-fetching children, if this is possible. |
|
| 166 | + foreach (array_merge($added, $modified) as $item) { |
|
| 167 | + $fullPath = $collectionUrl . '/' . $item; |
|
| 168 | + $fullPaths[] = $fullPath; |
|
| 169 | + } |
|
| 170 | + |
|
| 171 | + $responses = []; |
|
| 172 | + foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) { |
|
| 173 | + |
|
| 174 | + // The 'Property_Response' class is responsible for generating a |
|
| 175 | + // single {DAV:}response xml element. |
|
| 176 | + $responses[] = new DAV\Xml\Element\Response($fullPath, $props); |
|
| 177 | + |
|
| 178 | + } |
|
| 179 | + |
|
| 180 | + |
|
| 181 | + |
|
| 182 | + // Deleted items also show up as 'responses'. They have no properties, |
|
| 183 | + // and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'. |
|
| 184 | + foreach ($deleted as $item) { |
|
| 185 | + |
|
| 186 | + $fullPath = $collectionUrl . '/' . $item; |
|
| 187 | + $responses[] = new DAV\Xml\Element\Response($fullPath, [], 404); |
|
| 188 | + |
|
| 189 | + } |
|
| 190 | + $multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX . $syncToken); |
|
| 191 | + |
|
| 192 | + $this->server->httpResponse->setStatus(207); |
|
| 193 | + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); |
|
| 194 | + $this->server->httpResponse->setBody( |
|
| 195 | + $this->server->xml->write('{DAV:}multistatus', $multiStatus, $this->server->getBaseUri()) |
|
| 196 | + ); |
|
| 197 | + |
|
| 198 | + } |
|
| 199 | + |
|
| 200 | + /** |
|
| 201 | + * This method is triggered whenever properties are requested for a node. |
|
| 202 | + * We intercept this to see if we must return a {DAV:}sync-token. |
|
| 203 | + * |
|
| 204 | + * @param DAV\PropFind $propFind |
|
| 205 | + * @param DAV\INode $node |
|
| 206 | + * @return void |
|
| 207 | + */ |
|
| 208 | + public function propFind(DAV\PropFind $propFind, DAV\INode $node) { |
|
| 209 | + |
|
| 210 | + $propFind->handle('{DAV:}sync-token', function() use ($node) { |
|
| 211 | + if (!$node instanceof ISyncCollection || !$token = $node->getSyncToken()) { |
|
| 212 | + return; |
|
| 213 | + } |
|
| 214 | + return self::SYNCTOKEN_PREFIX . $token; |
|
| 215 | + }); |
|
| 216 | + |
|
| 217 | + } |
|
| 218 | + |
|
| 219 | + /** |
|
| 220 | + * The validateTokens event is triggered before every request. |
|
| 221 | + * |
|
| 222 | + * It's a moment where this plugin can check all the supplied lock tokens |
|
| 223 | + * in the If: header, and check if they are valid. |
|
| 224 | + * |
|
| 225 | + * @param RequestInterface $request |
|
| 226 | + * @param array $conditions |
|
| 227 | + * @return void |
|
| 228 | + */ |
|
| 229 | + public function validateTokens(RequestInterface $request, &$conditions) { |
|
| 230 | + |
|
| 231 | + foreach ($conditions as $kk => $condition) { |
|
| 232 | + |
|
| 233 | + foreach ($condition['tokens'] as $ii => $token) { |
|
| 234 | + |
|
| 235 | + // Sync-tokens must always start with our designated prefix. |
|
| 236 | + if (substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { |
|
| 237 | + continue; |
|
| 238 | + } |
|
| 239 | + |
|
| 240 | + // Checking if the token is a match. |
|
| 241 | + $node = $this->server->tree->getNodeForPath($condition['uri']); |
|
| 242 | + |
|
| 243 | + if ( |
|
| 244 | + $node instanceof ISyncCollection && |
|
| 245 | + $node->getSyncToken() == substr($token['token'], strlen(self::SYNCTOKEN_PREFIX)) |
|
| 246 | + ) { |
|
| 247 | + $conditions[$kk]['tokens'][$ii]['validToken'] = true; |
|
| 248 | + } |
|
| 249 | + |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + } |
|
| 253 | + |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + /** |
|
| 257 | + * Returns a bunch of meta-data about the plugin. |
|
| 258 | + * |
|
| 259 | + * Providing this information is optional, and is mainly displayed by the |
|
| 260 | + * Browser plugin. |
|
| 261 | + * |
|
| 262 | + * The description key in the returned array may contain html and will not |
|
| 263 | + * be sanitized. |
|
| 264 | + * |
|
| 265 | + * @return array |
|
| 266 | + */ |
|
| 267 | + public function getPluginInfo() { |
|
| 268 | + |
|
| 269 | + return [ |
|
| 270 | + 'name' => $this->getPluginName(), |
|
| 271 | + 'description' => 'Adds support for WebDAV Collection Sync (rfc6578)', |
|
| 272 | + 'link' => 'http://sabre.io/dav/sync/', |
|
| 273 | + ]; |
|
| 274 | + |
|
| 275 | + } |
|
| 276 | 276 | |
| 277 | 277 | } |
@@ -11,9 +11,9 @@ |
||
| 11 | 11 | */ |
| 12 | 12 | class Version { |
| 13 | 13 | |
| 14 | - /** |
|
| 15 | - * Full version number |
|
| 16 | - */ |
|
| 17 | - const VERSION = '3.1.3'; |
|
| 14 | + /** |
|
| 15 | + * Full version number |
|
| 16 | + */ |
|
| 17 | + const VERSION = '3.1.3'; |
|
| 18 | 18 | |
| 19 | 19 | } |
@@ -284,7 +284,9 @@ |
||
| 284 | 284 | } |
| 285 | 285 | } |
| 286 | 286 | // Removing the 404's for multi-status requests. |
| 287 | - if ($this->requestType === self::ALLPROPS) unset($r[404]); |
|
| 287 | + if ($this->requestType === self::ALLPROPS) { |
|
| 288 | + unset($r[404]); |
|
| 289 | + } |
|
| 288 | 290 | return $r; |
| 289 | 291 | |
| 290 | 292 | } |
@@ -10,338 +10,338 @@ |
||
| 10 | 10 | */ |
| 11 | 11 | class PropFind { |
| 12 | 12 | |
| 13 | - /** |
|
| 14 | - * A normal propfind |
|
| 15 | - */ |
|
| 16 | - const NORMAL = 0; |
|
| 17 | - |
|
| 18 | - /** |
|
| 19 | - * An allprops request. |
|
| 20 | - * |
|
| 21 | - * While this was originally intended for instructing the server to really |
|
| 22 | - * fetch every property, because it was used so often and it's so heavy |
|
| 23 | - * this turned into a small list of default properties after a while. |
|
| 24 | - * |
|
| 25 | - * So 'all properties' now means a hardcoded list. |
|
| 26 | - */ |
|
| 27 | - const ALLPROPS = 1; |
|
| 28 | - |
|
| 29 | - /** |
|
| 30 | - * A propname request. This just returns a list of properties that are |
|
| 31 | - * defined on a node, without their values. |
|
| 32 | - */ |
|
| 33 | - const PROPNAME = 2; |
|
| 34 | - |
|
| 35 | - /** |
|
| 36 | - * Creates the PROPFIND object |
|
| 37 | - * |
|
| 38 | - * @param string $path |
|
| 39 | - * @param array $properties |
|
| 40 | - * @param int $depth |
|
| 41 | - * @param int $requestType |
|
| 42 | - */ |
|
| 43 | - public function __construct($path, array $properties, $depth = 0, $requestType = self::NORMAL) { |
|
| 44 | - |
|
| 45 | - $this->path = $path; |
|
| 46 | - $this->properties = $properties; |
|
| 47 | - $this->depth = $depth; |
|
| 48 | - $this->requestType = $requestType; |
|
| 49 | - |
|
| 50 | - if ($requestType === self::ALLPROPS) { |
|
| 51 | - $this->properties = [ |
|
| 52 | - '{DAV:}getlastmodified', |
|
| 53 | - '{DAV:}getcontentlength', |
|
| 54 | - '{DAV:}resourcetype', |
|
| 55 | - '{DAV:}quota-used-bytes', |
|
| 56 | - '{DAV:}quota-available-bytes', |
|
| 57 | - '{DAV:}getetag', |
|
| 58 | - '{DAV:}getcontenttype', |
|
| 59 | - ]; |
|
| 60 | - } |
|
| 61 | - |
|
| 62 | - foreach ($this->properties as $propertyName) { |
|
| 63 | - |
|
| 64 | - // Seeding properties with 404's. |
|
| 65 | - $this->result[$propertyName] = [404, null]; |
|
| 66 | - |
|
| 67 | - } |
|
| 68 | - $this->itemsLeft = count($this->result); |
|
| 69 | - |
|
| 70 | - } |
|
| 71 | - |
|
| 72 | - /** |
|
| 73 | - * Handles a specific property. |
|
| 74 | - * |
|
| 75 | - * This method checks wether the specified property was requested in this |
|
| 76 | - * PROPFIND request, and if so, it will call the callback and use the |
|
| 77 | - * return value for it's value. |
|
| 78 | - * |
|
| 79 | - * Example: |
|
| 80 | - * |
|
| 81 | - * $propFind->handle('{DAV:}displayname', function() { |
|
| 82 | - * return 'hello'; |
|
| 83 | - * }); |
|
| 84 | - * |
|
| 85 | - * Note that handle will only work the first time. If null is returned, the |
|
| 86 | - * value is ignored. |
|
| 87 | - * |
|
| 88 | - * It's also possible to not pass a callback, but immediately pass a value |
|
| 89 | - * |
|
| 90 | - * @param string $propertyName |
|
| 91 | - * @param mixed $valueOrCallBack |
|
| 92 | - * @return void |
|
| 93 | - */ |
|
| 94 | - public function handle($propertyName, $valueOrCallBack) { |
|
| 95 | - |
|
| 96 | - if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { |
|
| 97 | - if (is_callable($valueOrCallBack)) { |
|
| 98 | - $value = $valueOrCallBack(); |
|
| 99 | - } else { |
|
| 100 | - $value = $valueOrCallBack; |
|
| 101 | - } |
|
| 102 | - if (!is_null($value)) { |
|
| 103 | - $this->itemsLeft--; |
|
| 104 | - $this->result[$propertyName] = [200, $value]; |
|
| 105 | - } |
|
| 106 | - } |
|
| 107 | - |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - /** |
|
| 111 | - * Sets the value of the property |
|
| 112 | - * |
|
| 113 | - * If status is not supplied, the status will default to 200 for non-null |
|
| 114 | - * properties, and 404 for null properties. |
|
| 115 | - * |
|
| 116 | - * @param string $propertyName |
|
| 117 | - * @param mixed $value |
|
| 118 | - * @param int $status |
|
| 119 | - * @return void |
|
| 120 | - */ |
|
| 121 | - public function set($propertyName, $value, $status = null) { |
|
| 122 | - |
|
| 123 | - if (is_null($status)) { |
|
| 124 | - $status = is_null($value) ? 404 : 200; |
|
| 125 | - } |
|
| 126 | - // If this is an ALLPROPS request and the property is |
|
| 127 | - // unknown, add it to the result; else ignore it: |
|
| 128 | - if (!isset($this->result[$propertyName])) { |
|
| 129 | - if ($this->requestType === self::ALLPROPS) { |
|
| 130 | - $this->result[$propertyName] = [$status, $value]; |
|
| 131 | - } |
|
| 132 | - return; |
|
| 133 | - } |
|
| 134 | - if ($status !== 404 && $this->result[$propertyName][0] === 404) { |
|
| 135 | - $this->itemsLeft--; |
|
| 136 | - } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) { |
|
| 137 | - $this->itemsLeft++; |
|
| 138 | - } |
|
| 139 | - $this->result[$propertyName] = [$status, $value]; |
|
| 140 | - |
|
| 141 | - } |
|
| 142 | - |
|
| 143 | - /** |
|
| 144 | - * Returns the current value for a property. |
|
| 145 | - * |
|
| 146 | - * @param string $propertyName |
|
| 147 | - * @return mixed |
|
| 148 | - */ |
|
| 149 | - public function get($propertyName) { |
|
| 150 | - |
|
| 151 | - return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; |
|
| 152 | - |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - /** |
|
| 156 | - * Returns the current status code for a property name. |
|
| 157 | - * |
|
| 158 | - * If the property does not appear in the list of requested properties, |
|
| 159 | - * null will be returned. |
|
| 160 | - * |
|
| 161 | - * @param string $propertyName |
|
| 162 | - * @return int|null |
|
| 163 | - */ |
|
| 164 | - public function getStatus($propertyName) { |
|
| 165 | - |
|
| 166 | - return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null; |
|
| 167 | - |
|
| 168 | - } |
|
| 169 | - |
|
| 170 | - /** |
|
| 171 | - * Updates the path for this PROPFIND. |
|
| 172 | - * |
|
| 173 | - * @param string $path |
|
| 174 | - * @return void |
|
| 175 | - */ |
|
| 176 | - public function setPath($path) { |
|
| 177 | - |
|
| 178 | - $this->path = $path; |
|
| 179 | - |
|
| 180 | - } |
|
| 181 | - |
|
| 182 | - /** |
|
| 183 | - * Returns the path this PROPFIND request is for. |
|
| 184 | - * |
|
| 185 | - * @return string |
|
| 186 | - */ |
|
| 187 | - public function getPath() { |
|
| 188 | - |
|
| 189 | - return $this->path; |
|
| 190 | - |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - /** |
|
| 194 | - * Returns the depth of this propfind request. |
|
| 195 | - * |
|
| 196 | - * @return int |
|
| 197 | - */ |
|
| 198 | - public function getDepth() { |
|
| 199 | - |
|
| 200 | - return $this->depth; |
|
| 201 | - |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - /** |
|
| 205 | - * Updates the depth of this propfind request. |
|
| 206 | - * |
|
| 207 | - * @param int $depth |
|
| 208 | - * @return void |
|
| 209 | - */ |
|
| 210 | - public function setDepth($depth) { |
|
| 211 | - |
|
| 212 | - $this->depth = $depth; |
|
| 213 | - |
|
| 214 | - } |
|
| 215 | - |
|
| 216 | - /** |
|
| 217 | - * Returns all propertynames that have a 404 status, and thus don't have a |
|
| 218 | - * value yet. |
|
| 219 | - * |
|
| 220 | - * @return array |
|
| 221 | - */ |
|
| 222 | - public function get404Properties() { |
|
| 223 | - |
|
| 224 | - if ($this->itemsLeft === 0) { |
|
| 225 | - return []; |
|
| 226 | - } |
|
| 227 | - $result = []; |
|
| 228 | - foreach ($this->result as $propertyName => $stuff) { |
|
| 229 | - if ($stuff[0] === 404) { |
|
| 230 | - $result[] = $propertyName; |
|
| 231 | - } |
|
| 232 | - } |
|
| 233 | - return $result; |
|
| 234 | - |
|
| 235 | - } |
|
| 236 | - |
|
| 237 | - /** |
|
| 238 | - * Returns the full list of requested properties. |
|
| 239 | - * |
|
| 240 | - * This returns just their names, not a status or value. |
|
| 241 | - * |
|
| 242 | - * @return array |
|
| 243 | - */ |
|
| 244 | - public function getRequestedProperties() { |
|
| 245 | - |
|
| 246 | - return $this->properties; |
|
| 247 | - |
|
| 248 | - } |
|
| 249 | - |
|
| 250 | - /** |
|
| 251 | - * Returns true if this was an '{DAV:}allprops' request. |
|
| 252 | - * |
|
| 253 | - * @return bool |
|
| 254 | - */ |
|
| 255 | - public function isAllProps() { |
|
| 256 | - |
|
| 257 | - return $this->requestType === self::ALLPROPS; |
|
| 258 | - |
|
| 259 | - } |
|
| 260 | - |
|
| 261 | - /** |
|
| 262 | - * Returns a result array that's often used in multistatus responses. |
|
| 263 | - * |
|
| 264 | - * The array uses status codes as keys, and property names and value pairs |
|
| 265 | - * as the value of the top array.. such as : |
|
| 266 | - * |
|
| 267 | - * [ |
|
| 268 | - * 200 => [ '{DAV:}displayname' => 'foo' ], |
|
| 269 | - * ] |
|
| 270 | - * |
|
| 271 | - * @return array |
|
| 272 | - */ |
|
| 273 | - public function getResultForMultiStatus() { |
|
| 274 | - |
|
| 275 | - $r = [ |
|
| 276 | - 200 => [], |
|
| 277 | - 404 => [], |
|
| 278 | - ]; |
|
| 279 | - foreach ($this->result as $propertyName => $info) { |
|
| 280 | - if (!isset($r[$info[0]])) { |
|
| 281 | - $r[$info[0]] = [$propertyName => $info[1]]; |
|
| 282 | - } else { |
|
| 283 | - $r[$info[0]][$propertyName] = $info[1]; |
|
| 284 | - } |
|
| 285 | - } |
|
| 286 | - // Removing the 404's for multi-status requests. |
|
| 287 | - if ($this->requestType === self::ALLPROPS) unset($r[404]); |
|
| 288 | - return $r; |
|
| 289 | - |
|
| 290 | - } |
|
| 291 | - |
|
| 292 | - /** |
|
| 293 | - * The path that we're fetching properties for. |
|
| 294 | - * |
|
| 295 | - * @var string |
|
| 296 | - */ |
|
| 297 | - protected $path; |
|
| 298 | - |
|
| 299 | - /** |
|
| 300 | - * The Depth of the request. |
|
| 301 | - * |
|
| 302 | - * 0 means only the current item. 1 means the current item + its children. |
|
| 303 | - * It can also be DEPTH_INFINITY if this is enabled in the server. |
|
| 304 | - * |
|
| 305 | - * @var int |
|
| 306 | - */ |
|
| 307 | - protected $depth = 0; |
|
| 308 | - |
|
| 309 | - /** |
|
| 310 | - * The type of request. See the TYPE constants |
|
| 311 | - */ |
|
| 312 | - protected $requestType; |
|
| 313 | - |
|
| 314 | - /** |
|
| 315 | - * A list of requested properties |
|
| 316 | - * |
|
| 317 | - * @var array |
|
| 318 | - */ |
|
| 319 | - protected $properties = []; |
|
| 320 | - |
|
| 321 | - /** |
|
| 322 | - * The result of the operation. |
|
| 323 | - * |
|
| 324 | - * The keys in this array are property names. |
|
| 325 | - * The values are an array with two elements: the http status code and then |
|
| 326 | - * optionally a value. |
|
| 327 | - * |
|
| 328 | - * Example: |
|
| 329 | - * |
|
| 330 | - * [ |
|
| 331 | - * "{DAV:}owner" : [404], |
|
| 332 | - * "{DAV:}displayname" : [200, "Admin"] |
|
| 333 | - * ] |
|
| 334 | - * |
|
| 335 | - * @var array |
|
| 336 | - */ |
|
| 337 | - protected $result = []; |
|
| 338 | - |
|
| 339 | - /** |
|
| 340 | - * This is used as an internal counter for the number of properties that do |
|
| 341 | - * not yet have a value. |
|
| 342 | - * |
|
| 343 | - * @var int |
|
| 344 | - */ |
|
| 345 | - protected $itemsLeft; |
|
| 13 | + /** |
|
| 14 | + * A normal propfind |
|
| 15 | + */ |
|
| 16 | + const NORMAL = 0; |
|
| 17 | + |
|
| 18 | + /** |
|
| 19 | + * An allprops request. |
|
| 20 | + * |
|
| 21 | + * While this was originally intended for instructing the server to really |
|
| 22 | + * fetch every property, because it was used so often and it's so heavy |
|
| 23 | + * this turned into a small list of default properties after a while. |
|
| 24 | + * |
|
| 25 | + * So 'all properties' now means a hardcoded list. |
|
| 26 | + */ |
|
| 27 | + const ALLPROPS = 1; |
|
| 28 | + |
|
| 29 | + /** |
|
| 30 | + * A propname request. This just returns a list of properties that are |
|
| 31 | + * defined on a node, without their values. |
|
| 32 | + */ |
|
| 33 | + const PROPNAME = 2; |
|
| 34 | + |
|
| 35 | + /** |
|
| 36 | + * Creates the PROPFIND object |
|
| 37 | + * |
|
| 38 | + * @param string $path |
|
| 39 | + * @param array $properties |
|
| 40 | + * @param int $depth |
|
| 41 | + * @param int $requestType |
|
| 42 | + */ |
|
| 43 | + public function __construct($path, array $properties, $depth = 0, $requestType = self::NORMAL) { |
|
| 44 | + |
|
| 45 | + $this->path = $path; |
|
| 46 | + $this->properties = $properties; |
|
| 47 | + $this->depth = $depth; |
|
| 48 | + $this->requestType = $requestType; |
|
| 49 | + |
|
| 50 | + if ($requestType === self::ALLPROPS) { |
|
| 51 | + $this->properties = [ |
|
| 52 | + '{DAV:}getlastmodified', |
|
| 53 | + '{DAV:}getcontentlength', |
|
| 54 | + '{DAV:}resourcetype', |
|
| 55 | + '{DAV:}quota-used-bytes', |
|
| 56 | + '{DAV:}quota-available-bytes', |
|
| 57 | + '{DAV:}getetag', |
|
| 58 | + '{DAV:}getcontenttype', |
|
| 59 | + ]; |
|
| 60 | + } |
|
| 61 | + |
|
| 62 | + foreach ($this->properties as $propertyName) { |
|
| 63 | + |
|
| 64 | + // Seeding properties with 404's. |
|
| 65 | + $this->result[$propertyName] = [404, null]; |
|
| 66 | + |
|
| 67 | + } |
|
| 68 | + $this->itemsLeft = count($this->result); |
|
| 69 | + |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + /** |
|
| 73 | + * Handles a specific property. |
|
| 74 | + * |
|
| 75 | + * This method checks wether the specified property was requested in this |
|
| 76 | + * PROPFIND request, and if so, it will call the callback and use the |
|
| 77 | + * return value for it's value. |
|
| 78 | + * |
|
| 79 | + * Example: |
|
| 80 | + * |
|
| 81 | + * $propFind->handle('{DAV:}displayname', function() { |
|
| 82 | + * return 'hello'; |
|
| 83 | + * }); |
|
| 84 | + * |
|
| 85 | + * Note that handle will only work the first time. If null is returned, the |
|
| 86 | + * value is ignored. |
|
| 87 | + * |
|
| 88 | + * It's also possible to not pass a callback, but immediately pass a value |
|
| 89 | + * |
|
| 90 | + * @param string $propertyName |
|
| 91 | + * @param mixed $valueOrCallBack |
|
| 92 | + * @return void |
|
| 93 | + */ |
|
| 94 | + public function handle($propertyName, $valueOrCallBack) { |
|
| 95 | + |
|
| 96 | + if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { |
|
| 97 | + if (is_callable($valueOrCallBack)) { |
|
| 98 | + $value = $valueOrCallBack(); |
|
| 99 | + } else { |
|
| 100 | + $value = $valueOrCallBack; |
|
| 101 | + } |
|
| 102 | + if (!is_null($value)) { |
|
| 103 | + $this->itemsLeft--; |
|
| 104 | + $this->result[$propertyName] = [200, $value]; |
|
| 105 | + } |
|
| 106 | + } |
|
| 107 | + |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + /** |
|
| 111 | + * Sets the value of the property |
|
| 112 | + * |
|
| 113 | + * If status is not supplied, the status will default to 200 for non-null |
|
| 114 | + * properties, and 404 for null properties. |
|
| 115 | + * |
|
| 116 | + * @param string $propertyName |
|
| 117 | + * @param mixed $value |
|
| 118 | + * @param int $status |
|
| 119 | + * @return void |
|
| 120 | + */ |
|
| 121 | + public function set($propertyName, $value, $status = null) { |
|
| 122 | + |
|
| 123 | + if (is_null($status)) { |
|
| 124 | + $status = is_null($value) ? 404 : 200; |
|
| 125 | + } |
|
| 126 | + // If this is an ALLPROPS request and the property is |
|
| 127 | + // unknown, add it to the result; else ignore it: |
|
| 128 | + if (!isset($this->result[$propertyName])) { |
|
| 129 | + if ($this->requestType === self::ALLPROPS) { |
|
| 130 | + $this->result[$propertyName] = [$status, $value]; |
|
| 131 | + } |
|
| 132 | + return; |
|
| 133 | + } |
|
| 134 | + if ($status !== 404 && $this->result[$propertyName][0] === 404) { |
|
| 135 | + $this->itemsLeft--; |
|
| 136 | + } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) { |
|
| 137 | + $this->itemsLeft++; |
|
| 138 | + } |
|
| 139 | + $this->result[$propertyName] = [$status, $value]; |
|
| 140 | + |
|
| 141 | + } |
|
| 142 | + |
|
| 143 | + /** |
|
| 144 | + * Returns the current value for a property. |
|
| 145 | + * |
|
| 146 | + * @param string $propertyName |
|
| 147 | + * @return mixed |
|
| 148 | + */ |
|
| 149 | + public function get($propertyName) { |
|
| 150 | + |
|
| 151 | + return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; |
|
| 152 | + |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + /** |
|
| 156 | + * Returns the current status code for a property name. |
|
| 157 | + * |
|
| 158 | + * If the property does not appear in the list of requested properties, |
|
| 159 | + * null will be returned. |
|
| 160 | + * |
|
| 161 | + * @param string $propertyName |
|
| 162 | + * @return int|null |
|
| 163 | + */ |
|
| 164 | + public function getStatus($propertyName) { |
|
| 165 | + |
|
| 166 | + return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null; |
|
| 167 | + |
|
| 168 | + } |
|
| 169 | + |
|
| 170 | + /** |
|
| 171 | + * Updates the path for this PROPFIND. |
|
| 172 | + * |
|
| 173 | + * @param string $path |
|
| 174 | + * @return void |
|
| 175 | + */ |
|
| 176 | + public function setPath($path) { |
|
| 177 | + |
|
| 178 | + $this->path = $path; |
|
| 179 | + |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + /** |
|
| 183 | + * Returns the path this PROPFIND request is for. |
|
| 184 | + * |
|
| 185 | + * @return string |
|
| 186 | + */ |
|
| 187 | + public function getPath() { |
|
| 188 | + |
|
| 189 | + return $this->path; |
|
| 190 | + |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + /** |
|
| 194 | + * Returns the depth of this propfind request. |
|
| 195 | + * |
|
| 196 | + * @return int |
|
| 197 | + */ |
|
| 198 | + public function getDepth() { |
|
| 199 | + |
|
| 200 | + return $this->depth; |
|
| 201 | + |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + /** |
|
| 205 | + * Updates the depth of this propfind request. |
|
| 206 | + * |
|
| 207 | + * @param int $depth |
|
| 208 | + * @return void |
|
| 209 | + */ |
|
| 210 | + public function setDepth($depth) { |
|
| 211 | + |
|
| 212 | + $this->depth = $depth; |
|
| 213 | + |
|
| 214 | + } |
|
| 215 | + |
|
| 216 | + /** |
|
| 217 | + * Returns all propertynames that have a 404 status, and thus don't have a |
|
| 218 | + * value yet. |
|
| 219 | + * |
|
| 220 | + * @return array |
|
| 221 | + */ |
|
| 222 | + public function get404Properties() { |
|
| 223 | + |
|
| 224 | + if ($this->itemsLeft === 0) { |
|
| 225 | + return []; |
|
| 226 | + } |
|
| 227 | + $result = []; |
|
| 228 | + foreach ($this->result as $propertyName => $stuff) { |
|
| 229 | + if ($stuff[0] === 404) { |
|
| 230 | + $result[] = $propertyName; |
|
| 231 | + } |
|
| 232 | + } |
|
| 233 | + return $result; |
|
| 234 | + |
|
| 235 | + } |
|
| 236 | + |
|
| 237 | + /** |
|
| 238 | + * Returns the full list of requested properties. |
|
| 239 | + * |
|
| 240 | + * This returns just their names, not a status or value. |
|
| 241 | + * |
|
| 242 | + * @return array |
|
| 243 | + */ |
|
| 244 | + public function getRequestedProperties() { |
|
| 245 | + |
|
| 246 | + return $this->properties; |
|
| 247 | + |
|
| 248 | + } |
|
| 249 | + |
|
| 250 | + /** |
|
| 251 | + * Returns true if this was an '{DAV:}allprops' request. |
|
| 252 | + * |
|
| 253 | + * @return bool |
|
| 254 | + */ |
|
| 255 | + public function isAllProps() { |
|
| 256 | + |
|
| 257 | + return $this->requestType === self::ALLPROPS; |
|
| 258 | + |
|
| 259 | + } |
|
| 260 | + |
|
| 261 | + /** |
|
| 262 | + * Returns a result array that's often used in multistatus responses. |
|
| 263 | + * |
|
| 264 | + * The array uses status codes as keys, and property names and value pairs |
|
| 265 | + * as the value of the top array.. such as : |
|
| 266 | + * |
|
| 267 | + * [ |
|
| 268 | + * 200 => [ '{DAV:}displayname' => 'foo' ], |
|
| 269 | + * ] |
|
| 270 | + * |
|
| 271 | + * @return array |
|
| 272 | + */ |
|
| 273 | + public function getResultForMultiStatus() { |
|
| 274 | + |
|
| 275 | + $r = [ |
|
| 276 | + 200 => [], |
|
| 277 | + 404 => [], |
|
| 278 | + ]; |
|
| 279 | + foreach ($this->result as $propertyName => $info) { |
|
| 280 | + if (!isset($r[$info[0]])) { |
|
| 281 | + $r[$info[0]] = [$propertyName => $info[1]]; |
|
| 282 | + } else { |
|
| 283 | + $r[$info[0]][$propertyName] = $info[1]; |
|
| 284 | + } |
|
| 285 | + } |
|
| 286 | + // Removing the 404's for multi-status requests. |
|
| 287 | + if ($this->requestType === self::ALLPROPS) unset($r[404]); |
|
| 288 | + return $r; |
|
| 289 | + |
|
| 290 | + } |
|
| 291 | + |
|
| 292 | + /** |
|
| 293 | + * The path that we're fetching properties for. |
|
| 294 | + * |
|
| 295 | + * @var string |
|
| 296 | + */ |
|
| 297 | + protected $path; |
|
| 298 | + |
|
| 299 | + /** |
|
| 300 | + * The Depth of the request. |
|
| 301 | + * |
|
| 302 | + * 0 means only the current item. 1 means the current item + its children. |
|
| 303 | + * It can also be DEPTH_INFINITY if this is enabled in the server. |
|
| 304 | + * |
|
| 305 | + * @var int |
|
| 306 | + */ |
|
| 307 | + protected $depth = 0; |
|
| 308 | + |
|
| 309 | + /** |
|
| 310 | + * The type of request. See the TYPE constants |
|
| 311 | + */ |
|
| 312 | + protected $requestType; |
|
| 313 | + |
|
| 314 | + /** |
|
| 315 | + * A list of requested properties |
|
| 316 | + * |
|
| 317 | + * @var array |
|
| 318 | + */ |
|
| 319 | + protected $properties = []; |
|
| 320 | + |
|
| 321 | + /** |
|
| 322 | + * The result of the operation. |
|
| 323 | + * |
|
| 324 | + * The keys in this array are property names. |
|
| 325 | + * The values are an array with two elements: the http status code and then |
|
| 326 | + * optionally a value. |
|
| 327 | + * |
|
| 328 | + * Example: |
|
| 329 | + * |
|
| 330 | + * [ |
|
| 331 | + * "{DAV:}owner" : [404], |
|
| 332 | + * "{DAV:}displayname" : [200, "Admin"] |
|
| 333 | + * ] |
|
| 334 | + * |
|
| 335 | + * @var array |
|
| 336 | + */ |
|
| 337 | + protected $result = []; |
|
| 338 | + |
|
| 339 | + /** |
|
| 340 | + * This is used as an internal counter for the number of properties that do |
|
| 341 | + * not yet have a value. |
|
| 342 | + * |
|
| 343 | + * @var int |
|
| 344 | + */ |
|
| 345 | + protected $itemsLeft; |
|
| 346 | 346 | |
| 347 | 347 | } |
@@ -15,77 +15,77 @@ |
||
| 15 | 15 | */ |
| 16 | 16 | class StringUtil { |
| 17 | 17 | |
| 18 | - /** |
|
| 19 | - * Checks if a needle occurs in a haystack ;) |
|
| 20 | - * |
|
| 21 | - * @param string $haystack |
|
| 22 | - * @param string $needle |
|
| 23 | - * @param string $collation |
|
| 24 | - * @param string $matchType |
|
| 25 | - * @return bool |
|
| 26 | - */ |
|
| 27 | - static function textMatch($haystack, $needle, $collation, $matchType = 'contains') { |
|
| 28 | - |
|
| 29 | - switch ($collation) { |
|
| 30 | - |
|
| 31 | - case 'i;ascii-casemap' : |
|
| 32 | - // default strtolower takes locale into consideration |
|
| 33 | - // we don't want this. |
|
| 34 | - $haystack = str_replace(range('a', 'z'), range('A', 'Z'), $haystack); |
|
| 35 | - $needle = str_replace(range('a', 'z'), range('A', 'Z'), $needle); |
|
| 36 | - break; |
|
| 37 | - |
|
| 38 | - case 'i;octet' : |
|
| 39 | - // Do nothing |
|
| 40 | - break; |
|
| 41 | - |
|
| 42 | - case 'i;unicode-casemap' : |
|
| 43 | - $haystack = mb_strtoupper($haystack, 'UTF-8'); |
|
| 44 | - $needle = mb_strtoupper($needle, 'UTF-8'); |
|
| 45 | - break; |
|
| 46 | - |
|
| 47 | - default : |
|
| 48 | - throw new Exception\BadRequest('Collation type: ' . $collation . ' is not supported'); |
|
| 49 | - |
|
| 50 | - } |
|
| 51 | - |
|
| 52 | - switch ($matchType) { |
|
| 53 | - |
|
| 54 | - case 'contains' : |
|
| 55 | - return strpos($haystack, $needle) !== false; |
|
| 56 | - case 'equals' : |
|
| 57 | - return $haystack === $needle; |
|
| 58 | - case 'starts-with' : |
|
| 59 | - return strpos($haystack, $needle) === 0; |
|
| 60 | - case 'ends-with' : |
|
| 61 | - return strrpos($haystack, $needle) === strlen($haystack) - strlen($needle); |
|
| 62 | - default : |
|
| 63 | - throw new Exception\BadRequest('Match-type: ' . $matchType . ' is not supported'); |
|
| 64 | - |
|
| 65 | - } |
|
| 66 | - |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - /** |
|
| 70 | - * This method takes an input string, checks if it's not valid UTF-8 and |
|
| 71 | - * attempts to convert it to UTF-8 if it's not. |
|
| 72 | - * |
|
| 73 | - * Note that currently this can only convert ISO-8559-1 to UTF-8 (latin-1), |
|
| 74 | - * anything else will likely fail. |
|
| 75 | - * |
|
| 76 | - * @param string $input |
|
| 77 | - * @return string |
|
| 78 | - */ |
|
| 79 | - static function ensureUTF8($input) { |
|
| 80 | - |
|
| 81 | - $encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true); |
|
| 82 | - |
|
| 83 | - if ($encoding === 'ISO-8859-1') { |
|
| 84 | - return utf8_encode($input); |
|
| 85 | - } else { |
|
| 86 | - return $input; |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - } |
|
| 18 | + /** |
|
| 19 | + * Checks if a needle occurs in a haystack ;) |
|
| 20 | + * |
|
| 21 | + * @param string $haystack |
|
| 22 | + * @param string $needle |
|
| 23 | + * @param string $collation |
|
| 24 | + * @param string $matchType |
|
| 25 | + * @return bool |
|
| 26 | + */ |
|
| 27 | + static function textMatch($haystack, $needle, $collation, $matchType = 'contains') { |
|
| 28 | + |
|
| 29 | + switch ($collation) { |
|
| 30 | + |
|
| 31 | + case 'i;ascii-casemap' : |
|
| 32 | + // default strtolower takes locale into consideration |
|
| 33 | + // we don't want this. |
|
| 34 | + $haystack = str_replace(range('a', 'z'), range('A', 'Z'), $haystack); |
|
| 35 | + $needle = str_replace(range('a', 'z'), range('A', 'Z'), $needle); |
|
| 36 | + break; |
|
| 37 | + |
|
| 38 | + case 'i;octet' : |
|
| 39 | + // Do nothing |
|
| 40 | + break; |
|
| 41 | + |
|
| 42 | + case 'i;unicode-casemap' : |
|
| 43 | + $haystack = mb_strtoupper($haystack, 'UTF-8'); |
|
| 44 | + $needle = mb_strtoupper($needle, 'UTF-8'); |
|
| 45 | + break; |
|
| 46 | + |
|
| 47 | + default : |
|
| 48 | + throw new Exception\BadRequest('Collation type: ' . $collation . ' is not supported'); |
|
| 49 | + |
|
| 50 | + } |
|
| 51 | + |
|
| 52 | + switch ($matchType) { |
|
| 53 | + |
|
| 54 | + case 'contains' : |
|
| 55 | + return strpos($haystack, $needle) !== false; |
|
| 56 | + case 'equals' : |
|
| 57 | + return $haystack === $needle; |
|
| 58 | + case 'starts-with' : |
|
| 59 | + return strpos($haystack, $needle) === 0; |
|
| 60 | + case 'ends-with' : |
|
| 61 | + return strrpos($haystack, $needle) === strlen($haystack) - strlen($needle); |
|
| 62 | + default : |
|
| 63 | + throw new Exception\BadRequest('Match-type: ' . $matchType . ' is not supported'); |
|
| 64 | + |
|
| 65 | + } |
|
| 66 | + |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + /** |
|
| 70 | + * This method takes an input string, checks if it's not valid UTF-8 and |
|
| 71 | + * attempts to convert it to UTF-8 if it's not. |
|
| 72 | + * |
|
| 73 | + * Note that currently this can only convert ISO-8559-1 to UTF-8 (latin-1), |
|
| 74 | + * anything else will likely fail. |
|
| 75 | + * |
|
| 76 | + * @param string $input |
|
| 77 | + * @return string |
|
| 78 | + */ |
|
| 79 | + static function ensureUTF8($input) { |
|
| 80 | + |
|
| 81 | + $encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true); |
|
| 82 | + |
|
| 83 | + if ($encoding === 'ISO-8859-1') { |
|
| 84 | + return utf8_encode($input); |
|
| 85 | + } else { |
|
| 86 | + return $input; |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + } |
|
| 90 | 90 | |
| 91 | 91 | } |